blob: c9d6f1fc09f165ec6136e09874f9720727e3a0c1 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000012#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080013#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080014#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080015#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080016#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070018#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000019#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000020#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000021#include "SkTLazy.h"
bsalomon16b99132015-08-13 14:55:50 -070022#include "batches/GrVertexBatch.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080024#include "gl/GrGLProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLGeometryProcessor.h"
26#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070031// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
34 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37};
38
39struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000040 SkPoint fPos;
41 SkPoint fOffset;
42 SkPoint fOuterRadii;
43 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000044};
45
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkPoint fPos;
48 SkPoint fOuterOffset;
49 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000050};
51
commit-bot@chromium.org81312832013-03-22 18:34:09 +000052inline bool circle_stays_circle(const SkMatrix& m) {
53 return m.isSimilarity();
54}
55
56}
57
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000058///////////////////////////////////////////////////////////////////////////////
59
60/**
bsalomonce1c8862014-12-15 07:11:22 -080061 * The output of this effect is a modulation of the input color and coverage for a circle. It
62 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
63 * with origin at the circle center. Two vertex attributes are used:
64 * vec2f : position in device space of the bounding geometry vertices
65 * vec4f : (p.xy, outerRad, innerRad)
66 * p is the position in the normalized space.
67 * outerRad is the outerRadius in device space.
68 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000069 */
70
joshualitt249af152014-09-15 11:41:13 -070071class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072public:
joshualittb8c241a2015-05-19 08:23:30 -070073 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
74 bool usesLocalCoords) {
75 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 }
77
joshualitt71c92602015-01-14 08:12:47 -080078 const Attribute* inPosition() const { return fInPosition; }
79 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070080 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -070081 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070082 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -070083 bool usesLocalCoords() const { return fUsesLocalCoords; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084 virtual ~CircleEdgeEffect() {}
85
mtklein36352bf2015-03-25 18:17:31 -070086 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000087
88 inline bool isStroked() const { return fStroke; }
89
joshualittb0a8a372014-09-23 09:50:21 -070090 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000091 public:
joshualitteb2a6762014-12-04 11:35:33 -080092 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080093 const GrBatchTracker&)
94 : 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>();
joshualitt9b989322014-12-15 14:16:27 -080098 GrGLGPBuilder* 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
joshualitt74077b92014-10-24 11:26:03 -0700104 GrGLVertToFrag v(kVec4f_GrSLType);
105 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,
joshualitt9b989322014-12-15 14:16:27 -0800133 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700134 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700135 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700136 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
137 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700138 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
139 key |= ce.colorIgnored() ? 0x4 : 0x0;
140 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000141 }
142
joshualitt9b989322014-12-15 14:16:27 -0800143 virtual void setData(const GrGLProgramDataManager& pdman,
144 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700145 const GrBatchTracker& bt) override {
joshualittb8c241a2015-05-19 08:23:30 -0700146 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
147 if (ce.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800148 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700149 GrColorToRGBAFloat(ce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800150 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700151 fColor = ce.color();
joshualitt9b989322014-12-15 14:16:27 -0800152 }
153 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154
joshualitte3ababe2015-05-15 07:56:07 -0700155 void setTransformData(const GrPrimitiveProcessor& primProc,
156 const GrGLProgramDataManager& pdman,
157 int index,
158 const SkTArray<const GrCoordTransform*, true>& transforms) override {
159 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
160 }
161
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000162 private:
joshualitt9b989322014-12-15 14:16:27 -0800163 GrColor fColor;
164 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700165 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000166 };
167
joshualitteb2a6762014-12-04 11:35:33 -0800168 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700169 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700170 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800171 GLProcessor::GenKey(*this, bt, caps, b);
172 }
173
joshualittabb52a12015-01-13 15:02:10 -0800174 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700175 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800176 return SkNEW_ARGS(GLProcessor, (*this, bt));
177 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000178
179private:
joshualittb8c241a2015-05-19 08:23:30 -0700180 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700181 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700182 , fLocalMatrix(localMatrix)
183 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800184 this->initClassID<CircleEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700185 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
186 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800187 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800188 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189 fStroke = stroke;
190 }
191
joshualitt88c23fc2015-05-13 14:18:07 -0700192 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700193 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800194 const Attribute* fInPosition;
195 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700197 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198
joshualittb0a8a372014-09-23 09:50:21 -0700199 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000200
joshualitt249af152014-09-15 11:41:13 -0700201 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202};
203
joshualittb0a8a372014-09-23 09:50:21 -0700204GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205
joshualitt0067ff52015-07-08 14:26:19 -0700206GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
207 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
208 d->fRandom->nextBool(),
209 GrTest::TestMatrix(d->fRandom),
210 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211}
212
213///////////////////////////////////////////////////////////////////////////////
214
215/**
216 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000217 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
218 * in both x and y directions.
219 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000220 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 */
222
joshualitt249af152014-09-15 11:41:13 -0700223class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224public:
joshualittb8c241a2015-05-19 08:23:30 -0700225 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
226 bool usesLocalCoords) {
227 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix, usesLocalCoords));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228 }
229
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230 virtual ~EllipseEdgeEffect() {}
231
mtklein36352bf2015-03-25 18:17:31 -0700232 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800233
joshualitt71c92602015-01-14 08:12:47 -0800234 const Attribute* inPosition() const { return fInPosition; }
235 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
236 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700237 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700238 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700239 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700240 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700241
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242 inline bool isStroked() const { return fStroke; }
243
joshualittb0a8a372014-09-23 09:50:21 -0700244 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245 public:
joshualitteb2a6762014-12-04 11:35:33 -0800246 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800247 const GrBatchTracker&)
248 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249
mtklein36352bf2015-03-25 18:17:31 -0700250 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800251 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800252 GrGLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800253 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254
joshualittabb52a12015-01-13 15:02:10 -0800255 // emit attributes
256 vsBuilder->emitAttributes(ee);
257
joshualitt74077b92014-10-24 11:26:03 -0700258 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
259 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700260 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800261 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000262
joshualitt74077b92014-10-24 11:26:03 -0700263 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
264 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
265 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800266 ee.inEllipseRadii()->fName);
267
joshualittb8c241a2015-05-19 08:23:30 -0700268 // setup pass through color
269 if (!ee.colorIgnored()) {
270 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
271 }
joshualitt9b989322014-12-15 14:16:27 -0800272
joshualittabb52a12015-01-13 15:02:10 -0800273 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700274 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800275
276 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800277 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800278 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800279
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000280 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700281 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700282 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
283 ellipseRadii.fsIn());
284 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
285 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
286 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
287
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000288 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700289 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
290 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
291 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000292
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000293 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800294 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700295 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
296 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
297 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
298 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
299 ellipseRadii.fsIn());
300 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
301 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000302 }
303
joshualitt2dd1ae02014-12-03 06:24:10 -0800304 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000305 }
306
robertphillips46d36f02015-01-18 08:14:14 -0800307 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800308 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700309 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700310 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700311 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
312 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700313 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
314 key |= ee.colorIgnored() ? 0x4 : 0x0;
315 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000316 }
317
joshualitt9b989322014-12-15 14:16:27 -0800318 virtual void setData(const GrGLProgramDataManager& pdman,
319 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700320 const GrBatchTracker& bt) override {
joshualittb8c241a2015-05-19 08:23:30 -0700321 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
322 if (ee.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800323 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700324 GrColorToRGBAFloat(ee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800325 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700326 fColor = ee.color();
joshualitt9b989322014-12-15 14:16:27 -0800327 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000328 }
329
joshualitte3ababe2015-05-15 07:56:07 -0700330 void setTransformData(const GrPrimitiveProcessor& primProc,
331 const GrGLProgramDataManager& pdman,
332 int index,
333 const SkTArray<const GrCoordTransform*, true>& transforms) override {
334 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
335 }
336
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000337 private:
joshualitt9b989322014-12-15 14:16:27 -0800338 GrColor fColor;
339 UniformHandle fColorUniform;
340
joshualitt249af152014-09-15 11:41:13 -0700341 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000342 };
343
joshualitteb2a6762014-12-04 11:35:33 -0800344 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700345 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700346 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800347 GLProcessor::GenKey(*this, bt, caps, b);
348 }
349
joshualittabb52a12015-01-13 15:02:10 -0800350 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700351 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800352 return SkNEW_ARGS(GLProcessor, (*this, bt));
353 }
354
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355private:
joshualittb8c241a2015-05-19 08:23:30 -0700356 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
357 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700358 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700359 , fLocalMatrix(localMatrix)
360 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800361 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800362 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
363 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800364 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800365 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800366 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367 fStroke = stroke;
368 }
369
joshualitt71c92602015-01-14 08:12:47 -0800370 const Attribute* fInPosition;
371 const Attribute* fInEllipseOffset;
372 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700373 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700374 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000375 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700376 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377
joshualittb0a8a372014-09-23 09:50:21 -0700378 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000379
joshualitt249af152014-09-15 11:41:13 -0700380 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000381};
382
joshualittb0a8a372014-09-23 09:50:21 -0700383GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384
joshualitt0067ff52015-07-08 14:26:19 -0700385GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
386 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
387 d->fRandom->nextBool(),
388 GrTest::TestMatrix(d->fRandom),
389 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000390}
391
392///////////////////////////////////////////////////////////////////////////////
393
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000394/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000395 * 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 +0000396 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
397 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
398 * using differentials.
399 *
400 * The result is device-independent and can be used with any affine matrix.
401 */
402
joshualitt249af152014-09-15 11:41:13 -0700403class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000404public:
405 enum Mode { kStroke = 0, kHairline, kFill };
406
joshualittb8c241a2015-05-19 08:23:30 -0700407 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
408 bool usesLocalCoords) {
409 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode, usesLocalCoords));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000410 }
411
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412 virtual ~DIEllipseEdgeEffect() {}
413
mtklein36352bf2015-03-25 18:17:31 -0700414 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000415
joshualitt71c92602015-01-14 08:12:47 -0800416 const Attribute* inPosition() const { return fInPosition; }
417 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
418 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700419 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700420 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte578a952015-05-14 10:09:13 -0700421 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700422 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700423
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 inline Mode getMode() const { return fMode; }
425
joshualittb0a8a372014-09-23 09:50:21 -0700426 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427 public:
joshualitteb2a6762014-12-04 11:35:33 -0800428 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800429 const GrBatchTracker&)
joshualitt5559ca22015-05-21 15:50:36 -0700430 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000431
mtklein36352bf2015-03-25 18:17:31 -0700432 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800433 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800434 GrGLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800435 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000436
joshualittabb52a12015-01-13 15:02:10 -0800437 // emit attributes
438 vsBuilder->emitAttributes(ee);
439
joshualitt74077b92014-10-24 11:26:03 -0700440 GrGLVertToFrag offsets0(kVec2f_GrSLType);
441 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700442 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800443 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700444
445 GrGLVertToFrag offsets1(kVec2f_GrSLType);
446 args.fPB->addVarying("EllipseOffsets1", &offsets1);
447 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800448 ee.inEllipseOffsets1()->fName);
449
joshualittb8c241a2015-05-19 08:23:30 -0700450 // setup pass through color
451 if (!ee.colorIgnored()) {
452 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
453 }
joshualitt9b989322014-12-15 14:16:27 -0800454
joshualittabb52a12015-01-13 15:02:10 -0800455 // Setup position
joshualitt5559ca22015-05-21 15:50:36 -0700456 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix(),
457 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800458
459 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800460 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualitte3ababe2015-05-15 07:56:07 -0700461 args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800462
egdaniel29bee0f2015-04-29 11:54:42 -0700463 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700464 SkAssertResult(fsBuilder->enableFeature(
465 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700467 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
468 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
469 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
470 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
471 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
472 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
473 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000474
joshualitt74077b92014-10-24 11:26:03 -0700475 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000476 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700477 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
478 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800479 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000480 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700481 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
482 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000483 } else {
joshualitt74077b92014-10-24 11:26:03 -0700484 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000485 }
486
487 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800488 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700489 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
490 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
491 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
492 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
493 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
494 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
495 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
496 offsets1.fsIn());
497 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
498 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000499 }
500
joshualitt2dd1ae02014-12-03 06:24:10 -0800501 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000502 }
503
robertphillips46d36f02015-01-18 08:14:14 -0800504 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800505 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700506 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700507 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800508 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800509 uint16_t key = ellipseEffect.getMode();
joshualittb8c241a2015-05-19 08:23:30 -0700510 key |= ellipseEffect.colorIgnored() << 9;
511 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
512 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 }
514
joshualitt9b989322014-12-15 14:16:27 -0800515 virtual void setData(const GrGLProgramDataManager& pdman,
516 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700517 const GrBatchTracker& bt) override {
joshualitte578a952015-05-14 10:09:13 -0700518 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
joshualitt5559ca22015-05-21 15:50:36 -0700519
520 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
521 fViewMatrix = dee.viewMatrix();
522 GrGLfloat viewMatrix[3 * 3];
523 GrGLGetMatrix<3>(viewMatrix, fViewMatrix);
524 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
525 }
joshualittee2af952014-12-30 09:04:15 -0800526
joshualittb8c241a2015-05-19 08:23:30 -0700527 if (dee.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800528 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700529 GrColorToRGBAFloat(dee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800530 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700531 fColor = dee.color();
joshualitt9b989322014-12-15 14:16:27 -0800532 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 }
534
535 private:
joshualitt5559ca22015-05-21 15:50:36 -0700536 SkMatrix fViewMatrix;
joshualitt9b989322014-12-15 14:16:27 -0800537 GrColor fColor;
538 UniformHandle fColorUniform;
joshualitt5559ca22015-05-21 15:50:36 -0700539 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800540
joshualitt249af152014-09-15 11:41:13 -0700541 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542 };
543
joshualitteb2a6762014-12-04 11:35:33 -0800544 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700545 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700546 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800547 GLProcessor::GenKey(*this, bt, caps, b);
548 }
549
joshualittabb52a12015-01-13 15:02:10 -0800550 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700551 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800552 return SkNEW_ARGS(GLProcessor, (*this, bt));
553 }
554
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000555private:
joshualittb8c241a2015-05-19 08:23:30 -0700556 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
557 bool usesLocalCoords)
joshualitte578a952015-05-14 10:09:13 -0700558 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700559 , fViewMatrix(viewMatrix)
560 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800561 this->initClassID<DIEllipseEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700562 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
563 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800564 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualittb8c241a2015-05-19 08:23:30 -0700565 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800566 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualittb8c241a2015-05-19 08:23:30 -0700567 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000568 fMode = mode;
569 }
570
joshualitt71c92602015-01-14 08:12:47 -0800571 const Attribute* fInPosition;
572 const Attribute* fInEllipseOffsets0;
573 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700574 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700575 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000576 Mode fMode;
joshualittb8c241a2015-05-19 08:23:30 -0700577 bool fUsesLocalCoords;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000578
joshualittb0a8a372014-09-23 09:50:21 -0700579 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000580
joshualitt249af152014-09-15 11:41:13 -0700581 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000582};
583
joshualittb0a8a372014-09-23 09:50:21 -0700584GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000585
joshualitt0067ff52015-07-08 14:26:19 -0700586GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
587 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
588 GrTest::TestMatrix(d->fRandom),
589 (Mode)(d->fRandom->nextRangeU(0,2)),
590 d->fRandom->nextBool());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000591}
592
593///////////////////////////////////////////////////////////////////////////////
594
robertphillipsea461502015-05-26 11:38:03 -0700595bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700596 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800597 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800598 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800599 bool useAA,
600 const SkRect& oval,
joshualittae3d63a2015-07-13 08:44:06 -0700601 const SkStrokeRec& stroke) {
602 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000603
604 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000605 return false;
606 }
607
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000608 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800609 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillipsea461502015-05-26 11:38:03 -0700610 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000611 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700612 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
robertphillipsea461502015-05-26 11:38:03 -0700613 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
614 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000615 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800616 } else if (viewMatrix.rectStaysRect()) {
robertphillipsea461502015-05-26 11:38:03 -0700617 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
618 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000619 } else {
620 return false;
621 }
622
623 return true;
624}
625
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000626///////////////////////////////////////////////////////////////////////////////
627
bsalomonabd30f52015-08-13 13:34:48 -0700628class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800629public:
630 struct Geometry {
631 GrColor fColor;
632 SkMatrix fViewMatrix;
633 SkScalar fInnerRadius;
634 SkScalar fOuterRadius;
635 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700636 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800637 };
638
bsalomonabd30f52015-08-13 13:34:48 -0700639 static GrDrawBatch* Create(const Geometry& geometry) {
640 return SkNEW_ARGS(CircleBatch, (geometry));
641 }
joshualitt76e7fb62015-02-11 08:52:27 -0800642
mtklein36352bf2015-03-25 18:17:31 -0700643 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800644
mtklein36352bf2015-03-25 18:17:31 -0700645 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800646 // When this is called on a batch, there is only one geometry bundle
647 out->setKnownFourComponents(fGeoData[0].fColor);
648 }
649
mtklein36352bf2015-03-25 18:17:31 -0700650 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800651 out->setUnknownSingleComponent();
652 }
653
bsalomone46f9fe2015-08-18 06:05:14 -0700654private:
bsalomon91d844d2015-08-10 10:47:29 -0700655 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800656 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700657 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800658 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800659 }
bsalomon91d844d2015-08-10 10:47:29 -0700660 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800661
662 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700663 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800664 fBatch.fColor = fGeoData[0].fColor;
665 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700666 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
667 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800668 }
669
bsalomon75398562015-08-17 12:55:38 -0700670 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800671 SkMatrix invert;
672 if (!this->viewMatrix().invert(&invert)) {
673 return;
674 }
675
676 // Setup geometry processor
677 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
678 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700679 invert,
680 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800681
bsalomon75398562015-08-17 12:55:38 -0700682 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800683
joshualitt76e7fb62015-02-11 08:52:27 -0800684 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800685 size_t vertexStride = gp->getVertexStride();
686 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700687 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700688 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700689 instanceCount));
690 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800691 return;
692 }
693
joshualitt76e7fb62015-02-11 08:52:27 -0800694 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700695 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800696
bsalomonb5238a72015-05-05 07:49:49 -0700697 SkScalar innerRadius = geom.fInnerRadius;
698 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800699
bsalomonb5238a72015-05-05 07:49:49 -0700700 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800701
702 // The inner radius in the vertex data must be specified in normalized space.
703 innerRadius = innerRadius / outerRadius;
704 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
705 verts[0].fOffset = SkPoint::Make(-1, -1);
706 verts[0].fOuterRadius = outerRadius;
707 verts[0].fInnerRadius = innerRadius;
708
709 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
710 verts[1].fOffset = SkPoint::Make(-1, 1);
711 verts[1].fOuterRadius = outerRadius;
712 verts[1].fInnerRadius = innerRadius;
713
714 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
715 verts[2].fOffset = SkPoint::Make(1, 1);
716 verts[2].fOuterRadius = outerRadius;
717 verts[2].fInnerRadius = innerRadius;
718
719 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
720 verts[3].fOffset = SkPoint::Make(1, -1);
721 verts[3].fOuterRadius = outerRadius;
722 verts[3].fInnerRadius = innerRadius;
723
bsalomonb5238a72015-05-05 07:49:49 -0700724 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800725 }
bsalomon75398562015-08-17 12:55:38 -0700726 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800727 }
728
729 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
730
joshualitt76e7fb62015-02-11 08:52:27 -0800731 CircleBatch(const Geometry& geometry) {
732 this->initClassID<CircleBatch>();
733 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700734
735 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800736 }
737
bsalomoncb02b382015-08-12 11:14:50 -0700738 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700739 CircleBatch* that = t->cast<CircleBatch>();
740 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
741 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700742 return false;
743 }
744
joshualitt76e7fb62015-02-11 08:52:27 -0800745 // TODO use vertex color to avoid breaking batches
746 if (this->color() != that->color()) {
747 return false;
748 }
749
750 if (this->stroke() != that->stroke()) {
751 return false;
752 }
753
754 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
755 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
756 return false;
757 }
758
759 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700760 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800761 return true;
762 }
763
764 GrColor color() const { return fBatch.fColor; }
765 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
766 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
767 bool stroke() const { return fBatch.fStroke; }
768
769 struct BatchTracker {
770 GrColor fColor;
771 bool fStroke;
772 bool fUsesLocalCoords;
773 bool fColorIgnored;
774 bool fCoverageIgnored;
775 };
776
joshualitt76e7fb62015-02-11 08:52:27 -0800777 BatchTracker fBatch;
778 SkSTArray<1, Geometry, true> fGeoData;
779};
780
bsalomonabd30f52015-08-13 13:34:48 -0700781static GrDrawBatch* create_circle_batch(GrColor color,
782 const SkMatrix& viewMatrix,
783 bool useCoverageAA,
784 const SkRect& circle,
785 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000786 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800787 viewMatrix.mapPoints(&center, 1);
788 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
789 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000790
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000791 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000792 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
793 SkStrokeRec::kHairline_Style == style;
794 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000795
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000796 SkScalar innerRadius = 0.0f;
797 SkScalar outerRadius = radius;
798 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000799 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000800 if (SkScalarNearlyZero(strokeWidth)) {
801 halfWidth = SK_ScalarHalf;
802 } else {
803 halfWidth = SkScalarHalf(strokeWidth);
804 }
805
806 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000807 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000808 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000809 }
810 }
811
bsalomonce1c8862014-12-15 07:11:22 -0800812 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
813 // computation because the computed alpha is zero, rather than 50%, at the radius.
814 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
815 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000816 outerRadius += SK_ScalarHalf;
817 innerRadius -= SK_ScalarHalf;
818
joshualitt76e7fb62015-02-11 08:52:27 -0800819 CircleBatch::Geometry geometry;
820 geometry.fViewMatrix = viewMatrix;
821 geometry.fColor = color;
822 geometry.fInnerRadius = innerRadius;
823 geometry.fOuterRadius = outerRadius;
824 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700825 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
826 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000827
joshualitt3e708c52015-04-30 13:49:27 -0700828 return CircleBatch::Create(geometry);
829}
830
robertphillipsea461502015-05-26 11:38:03 -0700831void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700832 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -0700833 GrColor color,
834 const SkMatrix& viewMatrix,
835 bool useCoverageAA,
836 const SkRect& circle,
837 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -0700838 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
839 stroke));
joshualittae3d63a2015-07-13 08:44:06 -0700840 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000841}
842
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000843///////////////////////////////////////////////////////////////////////////////
844
bsalomonabd30f52015-08-13 13:34:48 -0700845class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800846public:
847 struct Geometry {
848 GrColor fColor;
849 SkMatrix fViewMatrix;
850 SkScalar fXRadius;
851 SkScalar fYRadius;
852 SkScalar fInnerXRadius;
853 SkScalar fInnerYRadius;
854 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700855 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800856 };
857
bsalomonabd30f52015-08-13 13:34:48 -0700858 static GrDrawBatch* Create(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -0800859 return SkNEW_ARGS(EllipseBatch, (geometry));
860 }
861
mtklein36352bf2015-03-25 18:17:31 -0700862 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800863
mtklein36352bf2015-03-25 18:17:31 -0700864 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800865 // When this is called on a batch, there is only one geometry bundle
866 out->setKnownFourComponents(fGeoData[0].fColor);
867 }
mtklein36352bf2015-03-25 18:17:31 -0700868 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800869 out->setUnknownSingleComponent();
870 }
871
bsalomone46f9fe2015-08-18 06:05:14 -0700872private:
bsalomon91d844d2015-08-10 10:47:29 -0700873 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800874 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700875 if (!opt.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800876 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800877 }
bsalomon91d844d2015-08-10 10:47:29 -0700878 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800879
880 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700881 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800882 fBatch.fColor = fGeoData[0].fColor;
883 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700884 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
885 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800886 }
887
bsalomon75398562015-08-17 12:55:38 -0700888 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800889 SkMatrix invert;
890 if (!this->viewMatrix().invert(&invert)) {
891 return;
892 }
893
894 // Setup geometry processor
895 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
896 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700897 invert,
898 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800899
bsalomon75398562015-08-17 12:55:38 -0700900 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800901
joshualitt76e7fb62015-02-11 08:52:27 -0800902 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700903 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800904 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800905 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700906 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700907 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700908 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800909 return;
910 }
911
bsalomon8415abe2015-05-04 11:41:41 -0700912 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700913 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700914
bsalomonb5238a72015-05-05 07:49:49 -0700915 SkScalar xRadius = geom.fXRadius;
916 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800917
918 // Compute the reciprocals of the radii here to save time in the shader
919 SkScalar xRadRecip = SkScalarInvert(xRadius);
920 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700921 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
922 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800923
bsalomonb5238a72015-05-05 07:49:49 -0700924 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800925
926 // The inner radius in the vertex data must be specified in normalized space.
927 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
928 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
929 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
930 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
931
932 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
933 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
934 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
935 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
936
937 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
938 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
939 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
940 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
941
942 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
943 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
944 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
945 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
946
bsalomonb5238a72015-05-05 07:49:49 -0700947 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800948 }
bsalomon75398562015-08-17 12:55:38 -0700949 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800950 }
951
952 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
953
joshualitt76e7fb62015-02-11 08:52:27 -0800954 EllipseBatch(const Geometry& geometry) {
955 this->initClassID<EllipseBatch>();
956 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700957
958 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800959 }
960
bsalomoncb02b382015-08-12 11:14:50 -0700961 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700962 EllipseBatch* that = t->cast<EllipseBatch>();
963
964 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
965 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700966 return false;
967 }
968
joshualitt76e7fb62015-02-11 08:52:27 -0800969 // TODO use vertex color to avoid breaking batches
970 if (this->color() != that->color()) {
971 return false;
972 }
973
974 if (this->stroke() != that->stroke()) {
975 return false;
976 }
977
978 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
979 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
980 return false;
981 }
982
983 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700984 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800985 return true;
986 }
987
988 GrColor color() const { return fBatch.fColor; }
989 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
990 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
991 bool stroke() const { return fBatch.fStroke; }
992
993 struct BatchTracker {
994 GrColor fColor;
995 bool fStroke;
996 bool fUsesLocalCoords;
997 bool fColorIgnored;
998 bool fCoverageIgnored;
999 };
1000
joshualitt76e7fb62015-02-11 08:52:27 -08001001 BatchTracker fBatch;
1002 SkSTArray<1, Geometry, true> fGeoData;
1003};
1004
bsalomonabd30f52015-08-13 13:34:48 -07001005static GrDrawBatch* create_ellipse_batch(GrColor color,
1006 const SkMatrix& viewMatrix,
1007 bool useCoverageAA,
1008 const SkRect& ellipse,
1009 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001010#ifdef SK_DEBUG
1011 {
1012 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001013 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001014 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001015 }
1016#endif
1017
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001018 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001019 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001020 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001021 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1022 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001023 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1024 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1025 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1026 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001027
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001028 // do (potentially) anisotropic mapping of stroke
1029 SkVector scaledStroke;
1030 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001031 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1032 viewMatrix[SkMatrix::kMSkewY]));
1033 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1034 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001035
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001036 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001037 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1038 SkStrokeRec::kHairline_Style == style;
1039 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001040
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001041 SkScalar innerXRadius = 0;
1042 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001043 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001044 if (SkScalarNearlyZero(scaledStroke.length())) {
1045 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1046 } else {
1047 scaledStroke.scale(SK_ScalarHalf);
1048 }
1049
1050 // we only handle thick strokes for near-circular ellipses
1051 if (scaledStroke.length() > SK_ScalarHalf &&
1052 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001053 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001054 }
1055
1056 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1057 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1058 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001059 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001060 }
1061
1062 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001063 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001064 innerXRadius = xRadius - scaledStroke.fX;
1065 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001066 }
1067
1068 xRadius += scaledStroke.fX;
1069 yRadius += scaledStroke.fY;
1070 }
1071
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001072 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001073 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001074 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001075 xRadius += SK_ScalarHalf;
1076 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001077
joshualitt76e7fb62015-02-11 08:52:27 -08001078 EllipseBatch::Geometry geometry;
1079 geometry.fViewMatrix = viewMatrix;
1080 geometry.fColor = color;
1081 geometry.fXRadius = xRadius;
1082 geometry.fYRadius = yRadius;
1083 geometry.fInnerXRadius = innerXRadius;
1084 geometry.fInnerYRadius = innerYRadius;
1085 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001086 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1087 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001088
joshualitt3e708c52015-04-30 13:49:27 -07001089 return EllipseBatch::Create(geometry);
1090}
1091
robertphillipsea461502015-05-26 11:38:03 -07001092bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001093 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001094 GrColor color,
1095 const SkMatrix& viewMatrix,
1096 bool useCoverageAA,
1097 const SkRect& ellipse,
1098 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001099 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1100 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001101 if (!batch) {
1102 return false;
1103 }
1104
joshualittae3d63a2015-07-13 08:44:06 -07001105 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001106 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001107}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001108
joshualitt76e7fb62015-02-11 08:52:27 -08001109/////////////////////////////////////////////////////////////////////////////////////////////////
1110
bsalomonabd30f52015-08-13 13:34:48 -07001111class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001112public:
1113 struct Geometry {
1114 GrColor fColor;
1115 SkMatrix fViewMatrix;
1116 SkScalar fXRadius;
1117 SkScalar fYRadius;
1118 SkScalar fInnerXRadius;
1119 SkScalar fInnerYRadius;
1120 SkScalar fGeoDx;
1121 SkScalar fGeoDy;
1122 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001123 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001124 };
1125
bsalomonabd30f52015-08-13 13:34:48 -07001126 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
joshualitt99c7c072015-05-01 13:43:30 -07001127 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds));
joshualitt76e7fb62015-02-11 08:52:27 -08001128 }
1129
mtklein36352bf2015-03-25 18:17:31 -07001130 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001131
mtklein36352bf2015-03-25 18:17:31 -07001132 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001133 // When this is called on a batch, there is only one geometry bundle
1134 out->setKnownFourComponents(fGeoData[0].fColor);
1135 }
mtklein36352bf2015-03-25 18:17:31 -07001136 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001137 out->setUnknownSingleComponent();
1138 }
1139
bsalomone46f9fe2015-08-18 06:05:14 -07001140private:
1141
bsalomon91d844d2015-08-10 10:47:29 -07001142 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001143 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001144 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001145 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001146 }
bsalomon91d844d2015-08-10 10:47:29 -07001147 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001148
1149 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001150 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001151 fBatch.fColor = fGeoData[0].fColor;
1152 fBatch.fMode = fGeoData[0].fMode;
bsalomon91d844d2015-08-10 10:47:29 -07001153 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1154 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001155 }
1156
bsalomon75398562015-08-17 12:55:38 -07001157 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001158 // Setup geometry processor
1159 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1160 this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -07001161 this->mode(),
1162 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001163
bsalomon75398562015-08-17 12:55:38 -07001164 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001165
joshualitt76e7fb62015-02-11 08:52:27 -08001166 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001167 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001168 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001169 QuadHelper helper;
1170 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001171 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001172 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001173 return;
1174 }
1175
joshualitt76e7fb62015-02-11 08:52:27 -08001176 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001177 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001178
bsalomonb5238a72015-05-05 07:49:49 -07001179 SkScalar xRadius = geom.fXRadius;
1180 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001181
bsalomonb5238a72015-05-05 07:49:49 -07001182 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001183
1184 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001185 SkScalar offsetDx = geom.fGeoDx / xRadius;
1186 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001187
reed80ea19c2015-05-12 10:37:34 -07001188 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1189 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001190
1191 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1192 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1193 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1194
1195 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1196 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1197 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1198
1199 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1200 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1201 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1202
1203 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1204 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1205 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1206
bsalomonb5238a72015-05-05 07:49:49 -07001207 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001208 }
bsalomon75398562015-08-17 12:55:38 -07001209 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001210 }
1211
1212 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1213
joshualitt99c7c072015-05-01 13:43:30 -07001214 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) {
joshualitt76e7fb62015-02-11 08:52:27 -08001215 this->initClassID<DIEllipseBatch>();
1216 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001217
1218 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001219 }
1220
bsalomoncb02b382015-08-12 11:14:50 -07001221 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001222 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1223 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1224 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001225 return false;
1226 }
1227
joshualitt76e7fb62015-02-11 08:52:27 -08001228 // TODO use vertex color to avoid breaking batches
1229 if (this->color() != that->color()) {
1230 return false;
1231 }
1232
1233 if (this->mode() != that->mode()) {
1234 return false;
1235 }
1236
joshualittd96a67b2015-05-05 14:09:05 -07001237 // TODO rewrite to allow positioning on CPU
1238 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001239 return false;
1240 }
1241
1242 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001243 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001244 return true;
1245 }
1246
1247 GrColor color() const { return fBatch.fColor; }
1248 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1249 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1250 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1251
1252 struct BatchTracker {
1253 GrColor fColor;
1254 DIEllipseEdgeEffect::Mode fMode;
1255 bool fUsesLocalCoords;
1256 bool fColorIgnored;
1257 bool fCoverageIgnored;
1258 };
1259
joshualitt76e7fb62015-02-11 08:52:27 -08001260 BatchTracker fBatch;
1261 SkSTArray<1, Geometry, true> fGeoData;
1262};
1263
bsalomonabd30f52015-08-13 13:34:48 -07001264static GrDrawBatch* create_diellipse_batch(GrColor color,
1265 const SkMatrix& viewMatrix,
1266 bool useCoverageAA,
1267 const SkRect& ellipse,
1268 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001269 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001270 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001271 SkScalar yRadius = SkScalarHalf(ellipse.height());
1272
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001273 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001274 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001275 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001276 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001277 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1278
1279 SkScalar innerXRadius = 0;
1280 SkScalar innerYRadius = 0;
1281 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1282 SkScalar strokeWidth = stroke.getWidth();
1283
1284 if (SkScalarNearlyZero(strokeWidth)) {
1285 strokeWidth = SK_ScalarHalf;
1286 } else {
1287 strokeWidth *= SK_ScalarHalf;
1288 }
1289
1290 // we only handle thick strokes for near-circular ellipses
1291 if (strokeWidth > SK_ScalarHalf &&
1292 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001293 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001294 }
1295
1296 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1297 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1298 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001299 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001300 }
1301
1302 // set inner radius (if needed)
1303 if (SkStrokeRec::kStroke_Style == style) {
1304 innerXRadius = xRadius - strokeWidth;
1305 innerYRadius = yRadius - strokeWidth;
1306 }
1307
1308 xRadius += strokeWidth;
1309 yRadius += strokeWidth;
1310 }
1311 if (DIEllipseEdgeEffect::kStroke == mode) {
1312 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1313 DIEllipseEdgeEffect::kFill;
1314 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001315
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001316 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001317 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1318 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1319 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1320 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001321 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1322 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001323
joshualitt76e7fb62015-02-11 08:52:27 -08001324 DIEllipseBatch::Geometry geometry;
1325 geometry.fViewMatrix = viewMatrix;
1326 geometry.fColor = color;
1327 geometry.fXRadius = xRadius;
1328 geometry.fYRadius = yRadius;
1329 geometry.fInnerXRadius = innerXRadius;
1330 geometry.fInnerYRadius = innerYRadius;
1331 geometry.fGeoDx = geoDx;
1332 geometry.fGeoDy = geoDy;
1333 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001334 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1335 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001336
joshualittd96a67b2015-05-05 14:09:05 -07001337 SkRect devBounds = geometry.fBounds;
1338 viewMatrix.mapRect(&devBounds);
1339 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001340}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001341
robertphillipsea461502015-05-26 11:38:03 -07001342bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001343 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001344 GrColor color,
1345 const SkMatrix& viewMatrix,
1346 bool useCoverageAA,
1347 const SkRect& ellipse,
1348 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001349 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1350 ellipse, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001351 if (!batch) {
1352 return false;
1353 }
joshualittae3d63a2015-07-13 08:44:06 -07001354 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001355 return true;
1356}
1357
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001358///////////////////////////////////////////////////////////////////////////////
1359
1360static const uint16_t gRRectIndices[] = {
1361 // corners
1362 0, 1, 5, 0, 5, 4,
1363 2, 3, 7, 2, 7, 6,
1364 8, 9, 13, 8, 13, 12,
1365 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001366
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001367 // edges
1368 1, 2, 6, 1, 6, 5,
1369 4, 5, 9, 4, 9, 8,
1370 6, 7, 11, 6, 11, 10,
1371 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001372
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001373 // center
1374 // we place this at the end so that we can ignore these indices when rendering stroke-only
1375 5, 6, 10, 5, 10, 9
1376};
1377
joshualitt5ead6da2014-10-22 16:00:29 -07001378static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1379static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1380static const int kVertsPerRRect = 16;
1381static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001382
bsalomoned0bcad2015-05-04 10:36:42 -07001383GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1384GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1385static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1386 GrResourceProvider* resourceProvider) {
1387 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1388 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1389 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001390 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001391 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1392 gStrokeRRectOnlyIndexBufferKey);
1393 } else {
bsalomoneae62002015-07-31 13:59:30 -07001394 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001395 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1396 gRRectOnlyIndexBufferKey);
1397
1398 }
1399}
1400
robertphillipsea461502015-05-26 11:38:03 -07001401bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001402 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001403 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001404 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001405 bool useAA,
1406 const SkRRect& origOuter,
1407 const SkRRect& origInner) {
joshualittae3d63a2015-07-13 08:44:06 -07001408 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt4421a4c2015-07-13 09:36:41 -07001409 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001410 if (!origInner.isEmpty()) {
1411 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001412 if (!viewMatrix.isIdentity()) {
1413 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001414 return false;
1415 }
1416 }
joshualittb0a8a372014-09-23 09:50:21 -07001417 GrPrimitiveEdgeType edgeType = applyAA ?
1418 kInverseFillAA_GrProcessorEdgeType :
1419 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001420 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001421 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1422 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001423 return false;
1424 }
joshualitt4421a4c2015-07-13 09:36:41 -07001425 arfps.set(&pipelineBuilder);
1426 arfps.addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001427 }
1428
1429 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
robertphillipsea461502015-05-26 11:38:03 -07001430 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001431 return true;
1432 }
1433
1434 SkASSERT(!origOuter.isEmpty());
1435 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001436 if (!viewMatrix.isIdentity()) {
1437 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001438 return false;
1439 }
1440 }
joshualittb0a8a372014-09-23 09:50:21 -07001441 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001442 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001443 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001444 if (NULL == effect) {
1445 return false;
1446 }
joshualitt4421a4c2015-07-13 09:36:41 -07001447 if (!arfps.isSet()) {
1448 arfps.set(&pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001449 }
joshualittd27f73e2014-12-29 07:43:36 -08001450
1451 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001452 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001453 return false;
1454 }
joshualittd27f73e2014-12-29 07:43:36 -08001455
joshualitt4421a4c2015-07-13 09:36:41 -07001456 arfps.addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001457 SkRect bounds = outer->getBounds();
1458 if (applyAA) {
1459 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1460 }
joshualittae3d63a2015-07-13 08:44:06 -07001461 target->drawBWRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001462 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001463}
1464
joshualitt76e7fb62015-02-11 08:52:27 -08001465///////////////////////////////////////////////////////////////////////////////////////////////////
1466
bsalomonabd30f52015-08-13 13:34:48 -07001467class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001468public:
1469 struct Geometry {
1470 GrColor fColor;
1471 SkMatrix fViewMatrix;
1472 SkScalar fInnerRadius;
1473 SkScalar fOuterRadius;
1474 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001475 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001476 };
1477
bsalomonabd30f52015-08-13 13:34:48 -07001478 static GrDrawBatch* Create(const Geometry& geometry) {
bsalomoned0bcad2015-05-04 10:36:42 -07001479 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001480 }
1481
mtklein36352bf2015-03-25 18:17:31 -07001482 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001483
mtklein36352bf2015-03-25 18:17:31 -07001484 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001485 // When this is called on a batch, there is only one geometry bundle
1486 out->setKnownFourComponents(fGeoData[0].fColor);
1487 }
mtklein36352bf2015-03-25 18:17:31 -07001488 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001489 out->setUnknownSingleComponent();
1490 }
1491
bsalomone46f9fe2015-08-18 06:05:14 -07001492private:
bsalomon91d844d2015-08-10 10:47:29 -07001493 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001494 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001495 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001496 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001497 }
bsalomon91d844d2015-08-10 10:47:29 -07001498 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001499
1500 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001501 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001502 fBatch.fColor = fGeoData[0].fColor;
1503 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001504 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1505 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001506 }
1507
bsalomon75398562015-08-17 12:55:38 -07001508 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001509 // reset to device coordinates
1510 SkMatrix invert;
1511 if (!this->viewMatrix().invert(&invert)) {
1512 SkDebugf("Failed to invert\n");
1513 return;
1514 }
1515
1516 // Setup geometry processor
1517 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1518 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001519 invert,
1520 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001521
bsalomon75398562015-08-17 12:55:38 -07001522 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001523
joshualitt76e7fb62015-02-11 08:52:27 -08001524 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001525 size_t vertexStride = gp->getVertexStride();
1526 SkASSERT(vertexStride == sizeof(CircleVertex));
1527
bsalomonb5238a72015-05-05 07:49:49 -07001528 // drop out the middle quad if we're stroked
1529 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001530 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001531 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001532
bsalomonb5238a72015-05-05 07:49:49 -07001533 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001534 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001535 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1536 indicesPerInstance, instanceCount));
1537 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001538 SkDebugf("Could not allocate vertices\n");
1539 return;
1540 }
1541
joshualitt76e7fb62015-02-11 08:52:27 -08001542 for (int i = 0; i < instanceCount; i++) {
1543 Geometry& args = fGeoData[i];
1544
1545 SkScalar outerRadius = args.fOuterRadius;
1546
egdanielbc227142015-04-21 06:28:08 -07001547 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001548
1549 SkScalar yCoords[4] = {
1550 bounds.fTop,
1551 bounds.fTop + outerRadius,
1552 bounds.fBottom - outerRadius,
1553 bounds.fBottom
1554 };
1555
1556 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1557 // The inner radius in the vertex data must be specified in normalized space.
1558 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1559 for (int i = 0; i < 4; ++i) {
1560 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1561 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1562 verts->fOuterRadius = outerRadius;
1563 verts->fInnerRadius = innerRadius;
1564 verts++;
1565
1566 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1567 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1568 verts->fOuterRadius = outerRadius;
1569 verts->fInnerRadius = innerRadius;
1570 verts++;
1571
1572 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1573 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1574 verts->fOuterRadius = outerRadius;
1575 verts->fInnerRadius = innerRadius;
1576 verts++;
1577
1578 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1579 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1580 verts->fOuterRadius = outerRadius;
1581 verts->fInnerRadius = innerRadius;
1582 verts++;
1583 }
1584 }
1585
bsalomon75398562015-08-17 12:55:38 -07001586 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001587 }
1588
1589 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1590
bsalomoned0bcad2015-05-04 10:36:42 -07001591 RRectCircleRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001592 this->initClassID<RRectCircleRendererBatch>();
1593 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001594
1595 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001596 }
1597
bsalomoncb02b382015-08-12 11:14:50 -07001598 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001599 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1600 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1601 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001602 return false;
1603 }
1604
joshualitt76e7fb62015-02-11 08:52:27 -08001605 // TODO use vertex color to avoid breaking batches
1606 if (this->color() != that->color()) {
1607 return false;
1608 }
1609
1610 if (this->stroke() != that->stroke()) {
1611 return false;
1612 }
1613
1614 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1615 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1616 return false;
1617 }
1618
1619 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001620 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001621 return true;
1622 }
1623
1624 GrColor color() const { return fBatch.fColor; }
1625 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1626 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1627 bool stroke() const { return fBatch.fStroke; }
1628
1629 struct BatchTracker {
1630 GrColor fColor;
1631 bool fStroke;
1632 bool fUsesLocalCoords;
1633 bool fColorIgnored;
1634 bool fCoverageIgnored;
1635 };
1636
joshualitt76e7fb62015-02-11 08:52:27 -08001637 BatchTracker fBatch;
1638 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001639};
1640
bsalomonabd30f52015-08-13 13:34:48 -07001641class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001642public:
1643 struct Geometry {
1644 GrColor fColor;
1645 SkMatrix fViewMatrix;
1646 SkScalar fXRadius;
1647 SkScalar fYRadius;
1648 SkScalar fInnerXRadius;
1649 SkScalar fInnerYRadius;
1650 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001651 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001652 };
1653
bsalomonabd30f52015-08-13 13:34:48 -07001654 static GrDrawBatch* Create(const Geometry& geometry) {
bsalomoned0bcad2015-05-04 10:36:42 -07001655 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry));
joshualitt76e7fb62015-02-11 08:52:27 -08001656 }
1657
mtklein36352bf2015-03-25 18:17:31 -07001658 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001659
mtklein36352bf2015-03-25 18:17:31 -07001660 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001661 // When this is called on a batch, there is only one geometry bundle
1662 out->setKnownFourComponents(fGeoData[0].fColor);
1663 }
mtklein36352bf2015-03-25 18:17:31 -07001664 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001665 out->setUnknownSingleComponent();
1666 }
1667
bsalomone46f9fe2015-08-18 06:05:14 -07001668private:
bsalomon91d844d2015-08-10 10:47:29 -07001669 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001670 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001671 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001672 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001673 }
bsalomon91d844d2015-08-10 10:47:29 -07001674 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001675
1676 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001677 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001678 fBatch.fColor = fGeoData[0].fColor;
1679 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001680 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1681 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001682 }
1683
bsalomon75398562015-08-17 12:55:38 -07001684 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001685 // reset to device coordinates
1686 SkMatrix invert;
1687 if (!this->viewMatrix().invert(&invert)) {
1688 SkDebugf("Failed to invert\n");
1689 return;
1690 }
1691
1692 // Setup geometry processor
1693 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1694 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001695 invert,
1696 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001697
bsalomon75398562015-08-17 12:55:38 -07001698 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001699
joshualitt76e7fb62015-02-11 08:52:27 -08001700 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001701 size_t vertexStride = gp->getVertexStride();
1702 SkASSERT(vertexStride == sizeof(EllipseVertex));
1703
bsalomonb5238a72015-05-05 07:49:49 -07001704 // drop out the middle quad if we're stroked
1705 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001706 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001707 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001708
bsalomonb5238a72015-05-05 07:49:49 -07001709 InstancedHelper helper;
1710 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001711 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001712 kVertsPerRRect, indicesPerInstance, instanceCount));
1713 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001714 SkDebugf("Could not allocate vertices\n");
1715 return;
1716 }
1717
joshualitt76e7fb62015-02-11 08:52:27 -08001718 for (int i = 0; i < instanceCount; i++) {
1719 Geometry& args = fGeoData[i];
1720
1721 // Compute the reciprocals of the radii here to save time in the shader
1722 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1723 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1724 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1725 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1726
1727 // Extend the radii out half a pixel to antialias.
1728 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1729 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1730
egdanielbc227142015-04-21 06:28:08 -07001731 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001732
1733 SkScalar yCoords[4] = {
1734 bounds.fTop,
1735 bounds.fTop + yOuterRadius,
1736 bounds.fBottom - yOuterRadius,
1737 bounds.fBottom
1738 };
1739 SkScalar yOuterOffsets[4] = {
1740 yOuterRadius,
1741 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1742 SK_ScalarNearlyZero,
1743 yOuterRadius
1744 };
1745
1746 for (int i = 0; i < 4; ++i) {
1747 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1748 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1749 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1750 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1751 verts++;
1752
1753 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1754 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1755 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1756 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1757 verts++;
1758
1759 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1760 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1761 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1762 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1763 verts++;
1764
1765 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1766 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1767 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1768 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1769 verts++;
1770 }
1771 }
bsalomon75398562015-08-17 12:55:38 -07001772 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001773 }
1774
1775 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1776
bsalomoned0bcad2015-05-04 10:36:42 -07001777 RRectEllipseRendererBatch(const Geometry& geometry) {
joshualitt76e7fb62015-02-11 08:52:27 -08001778 this->initClassID<RRectEllipseRendererBatch>();
1779 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001780
1781 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001782 }
1783
bsalomoncb02b382015-08-12 11:14:50 -07001784 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001785 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1786
1787 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1788 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001789 return false;
1790 }
1791
joshualitt76e7fb62015-02-11 08:52:27 -08001792 // TODO use vertex color to avoid breaking batches
1793 if (this->color() != that->color()) {
1794 return false;
1795 }
1796
1797 if (this->stroke() != that->stroke()) {
1798 return false;
1799 }
1800
1801 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1802 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1803 return false;
1804 }
1805
1806 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001807 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001808 return true;
1809 }
1810
1811 GrColor color() const { return fBatch.fColor; }
1812 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1813 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1814 bool stroke() const { return fBatch.fStroke; }
1815
1816 struct BatchTracker {
1817 GrColor fColor;
1818 bool fStroke;
1819 bool fUsesLocalCoords;
1820 bool fColorIgnored;
1821 bool fCoverageIgnored;
1822 };
1823
joshualitt76e7fb62015-02-11 08:52:27 -08001824 BatchTracker fBatch;
1825 SkSTArray<1, Geometry, true> fGeoData;
joshualitt76e7fb62015-02-11 08:52:27 -08001826};
1827
bsalomonabd30f52015-08-13 13:34:48 -07001828static GrDrawBatch* create_rrect_batch(GrColor color,
1829 const SkMatrix& viewMatrix,
1830 const SkRRect& rrect,
1831 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001832 SkASSERT(viewMatrix.rectStaysRect());
1833 SkASSERT(rrect.isSimple());
1834 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001835
joshualitt3e708c52015-04-30 13:49:27 -07001836 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001837 // do any matrix crunching before we reset the draw state for device coords
1838 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001839 SkRect bounds;
1840 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001841
1842 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001843 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1844 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1845 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1846 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001847
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001848 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001849
1850 // do (potentially) anisotropic mapping of stroke
1851 SkVector scaledStroke;
1852 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001853
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001854 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1855 SkStrokeRec::kHairline_Style == style;
1856 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1857
1858 if (hasStroke) {
1859 if (SkStrokeRec::kHairline_Style == style) {
1860 scaledStroke.set(1, 1);
1861 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001862 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1863 viewMatrix[SkMatrix::kMSkewY]));
1864 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1865 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001866 }
1867
1868 // if half of strokewidth is greater than radius, we don't handle that right now
1869 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001870 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001871 }
1872 }
1873
1874 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1875 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1876 // patch will have fractional coverage. This only matters when the interior is actually filled.
1877 // We could consider falling back to rect rendering here, since a tiny radius is
1878 // indistinguishable from a square corner.
1879 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001880 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001881 }
1882
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001883 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001884 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001885 SkScalar innerRadius = 0.0f;
1886 SkScalar outerRadius = xRadius;
1887 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001888 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001889 if (SkScalarNearlyZero(scaledStroke.fX)) {
1890 halfWidth = SK_ScalarHalf;
1891 } else {
1892 halfWidth = SkScalarHalf(scaledStroke.fX);
1893 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001894
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001895 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001896 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001897 }
1898 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001899 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001900 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001901
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001902 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001903
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001904 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001905 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1906 // Second, the outer radius is used to compute the verts of the bounding box that is
1907 // rendered and the outset ensures the box will cover all partially covered by the rrect
1908 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001909 outerRadius += SK_ScalarHalf;
1910 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001911
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001912 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001913 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001914
joshualitt76e7fb62015-02-11 08:52:27 -08001915 RRectCircleRendererBatch::Geometry geometry;
1916 geometry.fViewMatrix = viewMatrix;
1917 geometry.fColor = color;
1918 geometry.fInnerRadius = innerRadius;
1919 geometry.fOuterRadius = outerRadius;
1920 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001921 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001922
bsalomoned0bcad2015-05-04 10:36:42 -07001923 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001924 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001925 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001926 SkScalar innerXRadius = 0.0f;
1927 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001928 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001929 if (SkScalarNearlyZero(scaledStroke.length())) {
1930 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1931 } else {
1932 scaledStroke.scale(SK_ScalarHalf);
1933 }
1934
1935 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001936 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001937 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001938 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001939 }
1940
1941 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1942 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1943 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001944 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001945 }
1946
1947 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001948 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001949 innerXRadius = xRadius - scaledStroke.fX;
1950 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001951 }
1952
1953 xRadius += scaledStroke.fX;
1954 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001955 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001956 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001957
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001958 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001959
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001960 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001961 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001962
joshualitt76e7fb62015-02-11 08:52:27 -08001963 RRectEllipseRendererBatch::Geometry geometry;
1964 geometry.fViewMatrix = viewMatrix;
1965 geometry.fColor = color;
1966 geometry.fXRadius = xRadius;
1967 geometry.fYRadius = yRadius;
1968 geometry.fInnerXRadius = innerXRadius;
1969 geometry.fInnerYRadius = innerYRadius;
1970 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001971 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001972
bsalomoned0bcad2015-05-04 10:36:42 -07001973 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001974 }
joshualitt3e708c52015-04-30 13:49:27 -07001975}
1976
robertphillipsea461502015-05-26 11:38:03 -07001977bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001978 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001979 GrColor color,
1980 const SkMatrix& viewMatrix,
1981 bool useAA,
1982 const SkRRect& rrect,
1983 const SkStrokeRec& stroke) {
1984 if (rrect.isOval()) {
robertphillipsea461502015-05-26 11:38:03 -07001985 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
1986 stroke);
joshualitt3e708c52015-04-30 13:49:27 -07001987 }
1988
joshualittae3d63a2015-07-13 08:44:06 -07001989 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt3e708c52015-04-30 13:49:27 -07001990
1991 // only anti-aliased rrects for now
1992 if (!useCoverageAA) {
1993 return false;
1994 }
1995
1996 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
1997 return false;
1998 }
1999
bsalomonabd30f52015-08-13 13:34:48 -07002000 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002001 if (!batch) {
2002 return false;
2003 }
2004
joshualittae3d63a2015-07-13 08:44:06 -07002005 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002006 return true;
2007}
joshualitt3e708c52015-04-30 13:49:27 -07002008
2009///////////////////////////////////////////////////////////////////////////////////////////////////
2010
2011#ifdef GR_TEST_UTILS
2012
bsalomonabd30f52015-08-13 13:34:48 -07002013DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002014 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2015 GrColor color = GrRandomColor(random);
2016 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002017 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002018 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2019 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002020}
2021
bsalomonabd30f52015-08-13 13:34:48 -07002022DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002023 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2024 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002025 SkRect ellipse = GrTest::TestSquare(random);
2026 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002027 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002028}
2029
bsalomonabd30f52015-08-13 13:34:48 -07002030DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002031 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2032 GrColor color = GrRandomColor(random);
2033 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002034 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002035 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002036 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002037}
2038
bsalomonabd30f52015-08-13 13:34:48 -07002039DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002040 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2041 GrColor color = GrRandomColor(random);
2042 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002043 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002044}
2045
2046#endif