blob: b9303cf24debc1339e208c8952d462ff2fe1badb [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
joshualitt76e7fb62015-02-11 08:52:27 -080010#include "GrBatch.h"
11#include "GrBatchTarget.h"
joshualitt3e708c52015-04-30 13:49:27 -070012#include "GrBatchTest.h"
joshualitt76e7fb62015-02-11 08:52:27 -080013#include "GrBufferAllocPool.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080015#include "GrGeometryProcessor.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "GrGpu.h"
egdaniel605dd0f2014-11-12 08:35:25 -080017#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080018#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080019#include "GrProcessor.h"
bsalomon72e3ae42015-04-28 08:08:46 -070020#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000021#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000022#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000024#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLProcessor.h"
26#include "gl/GrGLSL.h"
27#include "gl/GrGLGeometryProcessor.h"
28#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000029
joshualitt76e7fb62015-02-11 08:52:27 -080030// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
31
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070033// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000034struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fPos;
36 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000037 SkScalar fOuterRadius;
38 SkScalar fInnerRadius;
39};
40
41struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPos;
43 SkPoint fOffset;
44 SkPoint fOuterRadii;
45 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000046};
47
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000048struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000049 SkPoint fPos;
50 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000052};
53
commit-bot@chromium.org81312832013-03-22 18:34:09 +000054inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56}
57
58}
59
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060///////////////////////////////////////////////////////////////////////////////
61
62/**
bsalomonce1c8862014-12-15 07:11:22 -080063 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
65 * with origin at the circle center. Two vertex attributes are used:
66 * vec2f : position in device space of the bounding geometry vertices
67 * vec4f : (p.xy, outerRad, innerRad)
68 * p is the position in the normalized space.
69 * outerRad is the outerRadius in device space.
70 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000071 */
72
joshualitt249af152014-09-15 11:41:13 -070073class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074public:
joshualittd27f73e2014-12-29 07:43:36 -080075 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
76 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 }
78
joshualitt71c92602015-01-14 08:12:47 -080079 const Attribute* inPosition() const { return fInPosition; }
80 const Attribute* inCircleEdge() const { return fInCircleEdge; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000081 virtual ~CircleEdgeEffect() {}
82
mtklein36352bf2015-03-25 18:17:31 -070083 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084
85 inline bool isStroked() const { return fStroke; }
86
joshualittb0a8a372014-09-23 09:50:21 -070087 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000088 public:
joshualitteb2a6762014-12-04 11:35:33 -080089 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080090 const GrBatchTracker&)
91 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092
mtklein36352bf2015-03-25 18:17:31 -070093 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080094 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080095 GrGLGPBuilder* pb = args.fPB;
96 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080097 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
98
joshualittabb52a12015-01-13 15:02:10 -080099 // emit attributes
100 vsBuilder->emitAttributes(ce);
101
joshualitt74077b92014-10-24 11:26:03 -0700102 GrGLVertToFrag v(kVec4f_GrSLType);
103 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800104 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105
joshualitt9b989322014-12-15 14:16:27 -0800106 // Setup pass through color
107 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
108 &fColorUniform);
109
joshualittabb52a12015-01-13 15:02:10 -0800110 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800111 this->setupPosition(pb, gpArgs, ce.inPosition()->fName, ce.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800112
113 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800114 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700115 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800116
egdaniel29bee0f2015-04-29 11:54:42 -0700117 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700118 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800119 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800120 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800121 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
122 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700123 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000125
joshualitt2dd1ae02014-12-03 06:24:10 -0800126 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000127 }
128
robertphillips46d36f02015-01-18 08:14:14 -0800129 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800130 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700131 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700132 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800133 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800134 const CircleEdgeEffect& circleEffect = gp.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800135 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800136 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
137 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800138 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000139 }
140
joshualitt9b989322014-12-15 14:16:27 -0800141 virtual void setData(const GrGLProgramDataManager& pdman,
142 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700143 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800144 this->setUniformViewMatrix(pdman, gp.viewMatrix());
145
joshualitt9b989322014-12-15 14:16:27 -0800146 const BatchTracker& local = bt.cast<BatchTracker>();
147 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
148 GrGLfloat c[4];
149 GrColorToRGBAFloat(local.fColor, c);
150 pdman.set4fv(fColorUniform, 1, c);
151 fColor = local.fColor;
152 }
153 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154
155 private:
joshualitt9b989322014-12-15 14:16:27 -0800156 GrColor fColor;
157 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700158 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000159 };
160
joshualitteb2a6762014-12-04 11:35:33 -0800161 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700162 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700163 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800164 GLProcessor::GenKey(*this, bt, caps, b);
165 }
166
joshualittabb52a12015-01-13 15:02:10 -0800167 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700168 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800169 return SkNEW_ARGS(GLProcessor, (*this, bt));
170 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171
mtklein36352bf2015-03-25 18:17:31 -0700172 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800173 BatchTracker* local = bt->cast<BatchTracker>();
174 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800175 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800176 }
177
joshualitt50cb76b2015-04-28 09:17:05 -0700178 bool onCanMakeEqual(const GrBatchTracker& m,
179 const GrGeometryProcessor& that,
180 const GrBatchTracker& t) const override {
181 const BatchTracker& mine = m.cast<BatchTracker>();
182 const BatchTracker& theirs = t.cast<BatchTracker>();
183 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
184 that, theirs.fUsesLocalCoords) &&
185 CanCombineOutput(mine.fInputColorType, mine.fColor,
186 theirs.fInputColorType, theirs.fColor);
187 }
188
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189private:
joshualittd27f73e2014-12-29 07:43:36 -0800190 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800191 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800192 this->initClassID<CircleEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800193 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
194 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800195 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196 fStroke = stroke;
197 }
198
joshualitt50cb76b2015-04-28 09:17:05 -0700199 bool onIsEqual(const GrGeometryProcessor& other) const override {
200 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
201 return cee.fStroke == fStroke;
202 }
203
204 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
205 out->setUnknownSingleComponent();
206 }
207
joshualitt9b989322014-12-15 14:16:27 -0800208 struct BatchTracker {
209 GrGPInput fInputColorType;
210 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800211 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800212 };
213
joshualitt71c92602015-01-14 08:12:47 -0800214 const Attribute* fInPosition;
215 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 bool fStroke;
217
joshualittb0a8a372014-09-23 09:50:21 -0700218 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
joshualitt249af152014-09-15 11:41:13 -0700220 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221};
222
joshualittb0a8a372014-09-23 09:50:21 -0700223GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
joshualittb0a8a372014-09-23 09:50:21 -0700225GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
226 GrContext* context,
227 const GrDrawTargetCaps&,
228 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800229 return CircleEdgeEffect::Create(GrRandomColor(random),
230 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700231 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232}
233
234///////////////////////////////////////////////////////////////////////////////
235
236/**
237 * 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 +0000238 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
239 * in both x and y directions.
240 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000241 * 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 +0000242 */
243
joshualitt249af152014-09-15 11:41:13 -0700244class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245public:
joshualittd27f73e2014-12-29 07:43:36 -0800246 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
247 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 }
249
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 virtual ~EllipseEdgeEffect() {}
251
mtklein36352bf2015-03-25 18:17:31 -0700252 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800253
joshualitt71c92602015-01-14 08:12:47 -0800254 const Attribute* inPosition() const { return fInPosition; }
255 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
256 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700257
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258 inline bool isStroked() const { return fStroke; }
259
joshualittb0a8a372014-09-23 09:50:21 -0700260 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261 public:
joshualitteb2a6762014-12-04 11:35:33 -0800262 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800263 const GrBatchTracker&)
264 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265
mtklein36352bf2015-03-25 18:17:31 -0700266 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800267 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800268 GrGLGPBuilder* pb = args.fPB;
269 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800270 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000271
joshualittabb52a12015-01-13 15:02:10 -0800272 // emit attributes
273 vsBuilder->emitAttributes(ee);
274
joshualitt74077b92014-10-24 11:26:03 -0700275 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
276 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700277 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800278 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000279
joshualitt74077b92014-10-24 11:26:03 -0700280 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
281 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
282 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800283 ee.inEllipseRadii()->fName);
284
joshualitt9b989322014-12-15 14:16:27 -0800285 // Setup pass through color
286 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
287 &fColorUniform);
288
joshualittabb52a12015-01-13 15:02:10 -0800289 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800290 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800291
292 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800293 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800294 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800295
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000296 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700297 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700298 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
299 ellipseRadii.fsIn());
300 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
301 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
302 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
303
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000304 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700305 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
306 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
307 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000308
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000309 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800310 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700311 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
312 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
313 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
314 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
315 ellipseRadii.fsIn());
316 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
317 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000318 }
319
joshualitt2dd1ae02014-12-03 06:24:10 -0800320 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000321 }
322
robertphillips46d36f02015-01-18 08:14:14 -0800323 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800324 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700325 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700326 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800327 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800328 const EllipseEdgeEffect& ellipseEffect = gp.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800329 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
robertphillips46d36f02015-01-18 08:14:14 -0800330 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x2 : 0x0;
331 key |= ComputePosKey(gp.viewMatrix()) << 2;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800332 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333 }
334
joshualitt9b989322014-12-15 14:16:27 -0800335 virtual void setData(const GrGLProgramDataManager& pdman,
336 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700337 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800338 this->setUniformViewMatrix(pdman, gp.viewMatrix());
339
joshualitt9b989322014-12-15 14:16:27 -0800340 const BatchTracker& local = bt.cast<BatchTracker>();
341 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
342 GrGLfloat c[4];
343 GrColorToRGBAFloat(local.fColor, c);
344 pdman.set4fv(fColorUniform, 1, c);
345 fColor = local.fColor;
346 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000347 }
348
349 private:
joshualitt9b989322014-12-15 14:16:27 -0800350 GrColor fColor;
351 UniformHandle fColorUniform;
352
joshualitt249af152014-09-15 11:41:13 -0700353 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000354 };
355
joshualitteb2a6762014-12-04 11:35:33 -0800356 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700357 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700358 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800359 GLProcessor::GenKey(*this, bt, caps, b);
360 }
361
joshualittabb52a12015-01-13 15:02:10 -0800362 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700363 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800364 return SkNEW_ARGS(GLProcessor, (*this, bt));
365 }
366
mtklein36352bf2015-03-25 18:17:31 -0700367 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800368 BatchTracker* local = bt->cast<BatchTracker>();
369 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800370 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800371 }
372
joshualitt50cb76b2015-04-28 09:17:05 -0700373 bool onCanMakeEqual(const GrBatchTracker& m,
374 const GrGeometryProcessor& that,
375 const GrBatchTracker& t) const override {
376 const BatchTracker& mine = m.cast<BatchTracker>();
377 const BatchTracker& theirs = t.cast<BatchTracker>();
378 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
379 that, theirs.fUsesLocalCoords) &&
380 CanCombineOutput(mine.fInputColorType, mine.fColor,
381 theirs.fInputColorType, theirs.fColor);
382 }
383
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384private:
joshualittd27f73e2014-12-29 07:43:36 -0800385 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800386 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800387 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800388 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
389 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt2dd1ae02014-12-03 06:24:10 -0800390 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800391 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt2dd1ae02014-12-03 06:24:10 -0800392 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000393 fStroke = stroke;
394 }
395
joshualitt50cb76b2015-04-28 09:17:05 -0700396 bool onIsEqual(const GrGeometryProcessor& other) const override {
397 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
398 return eee.fStroke == fStroke;
399 }
400
401 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
402 out->setUnknownSingleComponent();
403 }
404
joshualitt9b989322014-12-15 14:16:27 -0800405 struct BatchTracker {
406 GrGPInput fInputColorType;
407 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800408 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800409 };
410
joshualitt71c92602015-01-14 08:12:47 -0800411 const Attribute* fInPosition;
412 const Attribute* fInEllipseOffset;
413 const Attribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000414 bool fStroke;
415
joshualittb0a8a372014-09-23 09:50:21 -0700416 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000417
joshualitt249af152014-09-15 11:41:13 -0700418 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000419};
420
joshualittb0a8a372014-09-23 09:50:21 -0700421GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000422
joshualittb0a8a372014-09-23 09:50:21 -0700423GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
424 GrContext* context,
425 const GrDrawTargetCaps&,
426 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800427 return EllipseEdgeEffect::Create(GrRandomColor(random),
428 random->nextBool(),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700429 GrTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000430}
431
432///////////////////////////////////////////////////////////////////////////////
433
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000434/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000435 * 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 +0000436 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
437 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
438 * using differentials.
439 *
440 * The result is device-independent and can be used with any affine matrix.
441 */
442
joshualitt249af152014-09-15 11:41:13 -0700443class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444public:
445 enum Mode { kStroke = 0, kHairline, kFill };
446
joshualitt8059eb92014-12-29 15:10:07 -0800447 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
448 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 }
450
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000451 virtual ~DIEllipseEdgeEffect() {}
452
mtklein36352bf2015-03-25 18:17:31 -0700453 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000454
joshualitt71c92602015-01-14 08:12:47 -0800455 const Attribute* inPosition() const { return fInPosition; }
456 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
457 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700458
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000459 inline Mode getMode() const { return fMode; }
460
joshualittb0a8a372014-09-23 09:50:21 -0700461 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462 public:
joshualitteb2a6762014-12-04 11:35:33 -0800463 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800464 const GrBatchTracker&)
465 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466
mtklein36352bf2015-03-25 18:17:31 -0700467 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800468 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800469 GrGLGPBuilder* pb = args.fPB;
470 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800471 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472
joshualittabb52a12015-01-13 15:02:10 -0800473 // emit attributes
474 vsBuilder->emitAttributes(ee);
475
joshualitt74077b92014-10-24 11:26:03 -0700476 GrGLVertToFrag offsets0(kVec2f_GrSLType);
477 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700478 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800479 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700480
481 GrGLVertToFrag offsets1(kVec2f_GrSLType);
482 args.fPB->addVarying("EllipseOffsets1", &offsets1);
483 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800484 ee.inEllipseOffsets1()->fName);
485
joshualitt9b989322014-12-15 14:16:27 -0800486 // Setup pass through color
487 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
488 &fColorUniform);
489
joshualittabb52a12015-01-13 15:02:10 -0800490 // Setup position
joshualittdd219872015-02-12 14:48:42 -0800491 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix());
joshualittabb52a12015-01-13 15:02:10 -0800492
493 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800494 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800495 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800496
egdaniel29bee0f2015-04-29 11:54:42 -0700497 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700498 SkAssertResult(fsBuilder->enableFeature(
499 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700501 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
502 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
503 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
504 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
505 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
506 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
507 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000508
joshualitt74077b92014-10-24 11:26:03 -0700509 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000510 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700511 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
512 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800513 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700515 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
516 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 } else {
joshualitt74077b92014-10-24 11:26:03 -0700518 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000519 }
520
521 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800522 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700523 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
524 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
525 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
526 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
527 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
528 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
529 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
530 offsets1.fsIn());
531 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
532 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 }
534
joshualitt2dd1ae02014-12-03 06:24:10 -0800535 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000536 }
537
robertphillips46d36f02015-01-18 08:14:14 -0800538 static void GenKey(const GrGeometryProcessor& gp,
joshualitt9b989322014-12-15 14:16:27 -0800539 const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700540 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700541 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800542 const BatchTracker& local = bt.cast<BatchTracker>();
robertphillips46d36f02015-01-18 08:14:14 -0800543 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800544 uint16_t key = ellipseEffect.getMode();
robertphillips46d36f02015-01-18 08:14:14 -0800545 key |= local.fUsesLocalCoords && gp.localMatrix().hasPerspective() ? 0x1 << 8 : 0x0;
546 key |= ComputePosKey(gp.viewMatrix()) << 9;
joshualitt8fc6c2d2014-12-22 15:27:05 -0800547 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548 }
549
joshualitt9b989322014-12-15 14:16:27 -0800550 virtual void setData(const GrGLProgramDataManager& pdman,
551 const GrPrimitiveProcessor& gp,
mtklein36352bf2015-03-25 18:17:31 -0700552 const GrBatchTracker& bt) override {
joshualittee2af952014-12-30 09:04:15 -0800553 this->setUniformViewMatrix(pdman, gp.viewMatrix());
554
joshualitt9b989322014-12-15 14:16:27 -0800555 const BatchTracker& local = bt.cast<BatchTracker>();
556 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
557 GrGLfloat c[4];
558 GrColorToRGBAFloat(local.fColor, c);
559 pdman.set4fv(fColorUniform, 1, c);
560 fColor = local.fColor;
561 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000562 }
563
564 private:
joshualitt9b989322014-12-15 14:16:27 -0800565 GrColor fColor;
566 UniformHandle fColorUniform;
567
joshualitt249af152014-09-15 11:41:13 -0700568 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000569 };
570
joshualitteb2a6762014-12-04 11:35:33 -0800571 virtual void getGLProcessorKey(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700572 const GrGLSLCaps& caps,
mtklein36352bf2015-03-25 18:17:31 -0700573 GrProcessorKeyBuilder* b) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800574 GLProcessor::GenKey(*this, bt, caps, b);
575 }
576
joshualittabb52a12015-01-13 15:02:10 -0800577 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
jvanverthcfc18862015-04-28 08:48:20 -0700578 const GrGLSLCaps&) const override {
joshualitteb2a6762014-12-04 11:35:33 -0800579 return SkNEW_ARGS(GLProcessor, (*this, bt));
580 }
581
mtklein36352bf2015-03-25 18:17:31 -0700582 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override {
joshualitt9b989322014-12-15 14:16:27 -0800583 BatchTracker* local = bt->cast<BatchTracker>();
584 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800585 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800586 }
587
joshualitt50cb76b2015-04-28 09:17:05 -0700588 bool onCanMakeEqual(const GrBatchTracker& m,
589 const GrGeometryProcessor& that,
590 const GrBatchTracker& t) const override {
591 const BatchTracker& mine = m.cast<BatchTracker>();
592 const BatchTracker& theirs = t.cast<BatchTracker>();
593 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
594 that, theirs.fUsesLocalCoords) &&
595 CanCombineOutput(mine.fInputColorType, mine.fColor,
596 theirs.fInputColorType, theirs.fColor);
597 }
598
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000599private:
joshualitt8059eb92014-12-29 15:10:07 -0800600 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
601 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800602 this->initClassID<DIEllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800603 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
604 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualitt2dd1ae02014-12-03 06:24:10 -0800605 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800606 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualitt2dd1ae02014-12-03 06:24:10 -0800607 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000608 fMode = mode;
609 }
610
joshualitt50cb76b2015-04-28 09:17:05 -0700611 bool onIsEqual(const GrGeometryProcessor& other) const override {
612 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
613 return eee.fMode == fMode;
614 }
615
616 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
617 out->setUnknownSingleComponent();
618 }
619
joshualitt9b989322014-12-15 14:16:27 -0800620 struct BatchTracker {
621 GrGPInput fInputColorType;
622 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800623 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800624 };
625
joshualitt71c92602015-01-14 08:12:47 -0800626 const Attribute* fInPosition;
627 const Attribute* fInEllipseOffsets0;
628 const Attribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000629 Mode fMode;
630
joshualittb0a8a372014-09-23 09:50:21 -0700631 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000632
joshualitt249af152014-09-15 11:41:13 -0700633 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000634};
635
joshualittb0a8a372014-09-23 09:50:21 -0700636GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000637
joshualittb0a8a372014-09-23 09:50:21 -0700638GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
639 GrContext* context,
640 const GrDrawTargetCaps&,
641 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800642 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
joshualitt4eaf9ce2015-04-28 13:31:18 -0700643 GrTest::TestMatrix(random),
joshualitt8059eb92014-12-29 15:10:07 -0800644 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000645}
646
647///////////////////////////////////////////////////////////////////////////////
648
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000649void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000650 SkSafeSetNull(fRRectIndexBuffer);
joshualitt58a65442014-10-22 20:53:24 -0700651 SkSafeSetNull(fStrokeRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000652}
653
joshualitt9853cce2014-11-17 14:22:48 -0800654bool GrOvalRenderer::drawOval(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -0800655 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800656 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800657 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800658 bool useAA,
659 const SkRect& oval,
660 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000661{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000662 bool useCoverageAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -0800663 !pipelineBuilder->getRenderTarget()->isMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000664
665 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000666 return false;
667 }
668
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000669 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800670 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
egdaniel8dd688b2015-01-22 10:16:09 -0800671 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000672 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700673 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800674 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
joshualitt8059eb92014-12-29 15:10:07 -0800675 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000676 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800677 } else if (viewMatrix.rectStaysRect()) {
egdaniel8dd688b2015-01-22 10:16:09 -0800678 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
679 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000680 } else {
681 return false;
682 }
683
684 return true;
685}
686
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000687///////////////////////////////////////////////////////////////////////////////
688
joshualitt76e7fb62015-02-11 08:52:27 -0800689class CircleBatch : public GrBatch {
690public:
691 struct Geometry {
692 GrColor fColor;
693 SkMatrix fViewMatrix;
694 SkScalar fInnerRadius;
695 SkScalar fOuterRadius;
696 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700697 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800698 };
699
700 static GrBatch* Create(const Geometry& geometry) {
701 return SkNEW_ARGS(CircleBatch, (geometry));
702 }
703
mtklein36352bf2015-03-25 18:17:31 -0700704 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800705
mtklein36352bf2015-03-25 18:17:31 -0700706 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800707 // When this is called on a batch, there is only one geometry bundle
708 out->setKnownFourComponents(fGeoData[0].fColor);
709 }
710
mtklein36352bf2015-03-25 18:17:31 -0700711 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800712 out->setUnknownSingleComponent();
713 }
714
mtklein36352bf2015-03-25 18:17:31 -0700715 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800716 // Handle any color overrides
717 if (init.fColorIgnored) {
718 fGeoData[0].fColor = GrColor_ILLEGAL;
719 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
720 fGeoData[0].fColor = init.fOverrideColor;
721 }
722
723 // setup batch properties
724 fBatch.fColorIgnored = init.fColorIgnored;
725 fBatch.fColor = fGeoData[0].fColor;
726 fBatch.fStroke = fGeoData[0].fStroke;
727 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
728 fBatch.fCoverageIgnored = init.fCoverageIgnored;
729 }
730
mtklein36352bf2015-03-25 18:17:31 -0700731 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800732 SkMatrix invert;
733 if (!this->viewMatrix().invert(&invert)) {
734 return;
735 }
736
737 // Setup geometry processor
738 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
739 this->stroke(),
740 invert));
741
742 batchTarget->initDraw(gp, pipeline);
743
744 // TODO this is hacky, but the only way we have to initialize the GP is to use the
745 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
746 // everywhere we can remove this nastiness
747 GrPipelineInfo init;
748 init.fColorIgnored = fBatch.fColorIgnored;
749 init.fOverrideColor = GrColor_ILLEGAL;
750 init.fCoverageIgnored = fBatch.fCoverageIgnored;
751 init.fUsesLocalCoords = this->usesLocalCoords();
752 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
753
754 int instanceCount = fGeoData.count();
755 int vertexCount = kVertsPerCircle * instanceCount;
756 size_t vertexStride = gp->getVertexStride();
757 SkASSERT(vertexStride == sizeof(CircleVertex));
758
759 const GrVertexBuffer* vertexBuffer;
760 int firstVertex;
761
762 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
763 vertexCount,
764 &vertexBuffer,
765 &firstVertex);
766
joshualitt4b31de82015-03-05 14:33:41 -0800767 if (!vertices || !batchTarget->quadIndexBuffer()) {
768 SkDebugf("Could not allocate buffers\n");
769 return;
770 }
771
joshualitt76e7fb62015-02-11 08:52:27 -0800772 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
773
774 for (int i = 0; i < instanceCount; i++) {
775 Geometry& args = fGeoData[i];
776
777 SkScalar innerRadius = args.fInnerRadius;
778 SkScalar outerRadius = args.fOuterRadius;
779
egdanielbc227142015-04-21 06:28:08 -0700780 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800781
782 // The inner radius in the vertex data must be specified in normalized space.
783 innerRadius = innerRadius / outerRadius;
784 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
785 verts[0].fOffset = SkPoint::Make(-1, -1);
786 verts[0].fOuterRadius = outerRadius;
787 verts[0].fInnerRadius = innerRadius;
788
789 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
790 verts[1].fOffset = SkPoint::Make(-1, 1);
791 verts[1].fOuterRadius = outerRadius;
792 verts[1].fInnerRadius = innerRadius;
793
794 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
795 verts[2].fOffset = SkPoint::Make(1, 1);
796 verts[2].fOuterRadius = outerRadius;
797 verts[2].fInnerRadius = innerRadius;
798
799 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
800 verts[3].fOffset = SkPoint::Make(1, -1);
801 verts[3].fOuterRadius = outerRadius;
802 verts[3].fInnerRadius = innerRadius;
803
804 verts += kVertsPerCircle;
805 }
806
807 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
808
809 GrDrawTarget::DrawInfo drawInfo;
810 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
811 drawInfo.setStartVertex(0);
812 drawInfo.setStartIndex(0);
813 drawInfo.setVerticesPerInstance(kVertsPerCircle);
814 drawInfo.setIndicesPerInstance(kIndicesPerCircle);
815 drawInfo.adjustStartVertex(firstVertex);
816 drawInfo.setVertexBuffer(vertexBuffer);
817 drawInfo.setIndexBuffer(quadIndexBuffer);
818
819 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
820
821 while (instanceCount) {
822 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
823 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
824 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
825
826 batchTarget->draw(drawInfo);
827
828 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
829 instanceCount -= drawInfo.instanceCount();
830 }
831 }
832
833 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
834
835private:
836 CircleBatch(const Geometry& geometry) {
837 this->initClassID<CircleBatch>();
838 fGeoData.push_back(geometry);
839 }
840
mtklein36352bf2015-03-25 18:17:31 -0700841 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800842 CircleBatch* that = t->cast<CircleBatch>();
843
844 // TODO use vertex color to avoid breaking batches
845 if (this->color() != that->color()) {
846 return false;
847 }
848
849 if (this->stroke() != that->stroke()) {
850 return false;
851 }
852
853 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
854 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
855 return false;
856 }
857
858 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
859 return true;
860 }
861
862 GrColor color() const { return fBatch.fColor; }
863 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
864 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
865 bool stroke() const { return fBatch.fStroke; }
866
867 struct BatchTracker {
868 GrColor fColor;
869 bool fStroke;
870 bool fUsesLocalCoords;
871 bool fColorIgnored;
872 bool fCoverageIgnored;
873 };
874
875 static const int kVertsPerCircle = 4;
876 static const int kIndicesPerCircle = 6;
877
joshualitt76e7fb62015-02-11 08:52:27 -0800878 BatchTracker fBatch;
879 SkSTArray<1, Geometry, true> fGeoData;
880};
881
joshualitt3e708c52015-04-30 13:49:27 -0700882static GrBatch* create_circle_batch(GrColor color,
883 const SkMatrix& viewMatrix,
884 bool useCoverageAA,
885 const SkRect& circle,
886 const SkStrokeRec& stroke,
887 SkRect* bounds) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000888 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800889 viewMatrix.mapPoints(&center, 1);
890 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
891 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000892
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000893 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000894 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
895 SkStrokeRec::kHairline_Style == style;
896 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000897
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000898 SkScalar innerRadius = 0.0f;
899 SkScalar outerRadius = radius;
900 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000901 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000902 if (SkScalarNearlyZero(strokeWidth)) {
903 halfWidth = SK_ScalarHalf;
904 } else {
905 halfWidth = SkScalarHalf(strokeWidth);
906 }
907
908 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000909 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000910 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000911 }
912 }
913
bsalomonce1c8862014-12-15 07:11:22 -0800914 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
915 // computation because the computed alpha is zero, rather than 50%, at the radius.
916 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
917 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000918 outerRadius += SK_ScalarHalf;
919 innerRadius -= SK_ScalarHalf;
920
joshualitt3e708c52015-04-30 13:49:27 -0700921 bounds->setLTRB(center.fX - outerRadius, center.fY - outerRadius,
922 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000923
joshualitt76e7fb62015-02-11 08:52:27 -0800924 CircleBatch::Geometry geometry;
925 geometry.fViewMatrix = viewMatrix;
926 geometry.fColor = color;
927 geometry.fInnerRadius = innerRadius;
928 geometry.fOuterRadius = outerRadius;
929 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualitt3e708c52015-04-30 13:49:27 -0700930 geometry.fDevBounds = *bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000931
joshualitt3e708c52015-04-30 13:49:27 -0700932 return CircleBatch::Create(geometry);
933}
934
935void GrOvalRenderer::drawCircle(GrDrawTarget* target,
936 GrPipelineBuilder* pipelineBuilder,
937 GrColor color,
938 const SkMatrix& viewMatrix,
939 bool useCoverageAA,
940 const SkRect& circle,
941 const SkStrokeRec& stroke) {
942 SkRect bounds;
943 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
944 stroke, &bounds));
joshualitt76e7fb62015-02-11 08:52:27 -0800945 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000946}
947
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000948///////////////////////////////////////////////////////////////////////////////
949
joshualitt76e7fb62015-02-11 08:52:27 -0800950class EllipseBatch : public GrBatch {
951public:
952 struct Geometry {
953 GrColor fColor;
954 SkMatrix fViewMatrix;
955 SkScalar fXRadius;
956 SkScalar fYRadius;
957 SkScalar fInnerXRadius;
958 SkScalar fInnerYRadius;
959 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -0700960 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800961 };
962
963 static GrBatch* Create(const Geometry& geometry) {
964 return SkNEW_ARGS(EllipseBatch, (geometry));
965 }
966
mtklein36352bf2015-03-25 18:17:31 -0700967 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800968
mtklein36352bf2015-03-25 18:17:31 -0700969 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800970 // When this is called on a batch, there is only one geometry bundle
971 out->setKnownFourComponents(fGeoData[0].fColor);
972 }
mtklein36352bf2015-03-25 18:17:31 -0700973 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800974 out->setUnknownSingleComponent();
975 }
976
mtklein36352bf2015-03-25 18:17:31 -0700977 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800978 // Handle any color overrides
979 if (init.fColorIgnored) {
980 fGeoData[0].fColor = GrColor_ILLEGAL;
981 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
982 fGeoData[0].fColor = init.fOverrideColor;
983 }
984
985 // setup batch properties
986 fBatch.fColorIgnored = init.fColorIgnored;
987 fBatch.fColor = fGeoData[0].fColor;
988 fBatch.fStroke = fGeoData[0].fStroke;
989 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
990 fBatch.fCoverageIgnored = init.fCoverageIgnored;
991 }
992
mtklein36352bf2015-03-25 18:17:31 -0700993 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800994 SkMatrix invert;
995 if (!this->viewMatrix().invert(&invert)) {
996 return;
997 }
998
999 // Setup geometry processor
1000 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1001 this->stroke(),
1002 invert));
joshualitt76e7fb62015-02-11 08:52:27 -08001003
1004 batchTarget->initDraw(gp, pipeline);
1005
1006 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1007 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1008 // everywhere we can remove this nastiness
1009 GrPipelineInfo init;
1010 init.fColorIgnored = fBatch.fColorIgnored;
1011 init.fOverrideColor = GrColor_ILLEGAL;
1012 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1013 init.fUsesLocalCoords = this->usesLocalCoords();
1014 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1015
1016 int instanceCount = fGeoData.count();
1017 int vertexCount = kVertsPerEllipse * instanceCount;
1018 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001019 SkASSERT(vertexStride == sizeof(EllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001020
1021 const GrVertexBuffer* vertexBuffer;
1022 int firstVertex;
1023
1024 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1025 vertexCount,
1026 &vertexBuffer,
1027 &firstVertex);
1028
joshualitt4b31de82015-03-05 14:33:41 -08001029 if (!vertices || !batchTarget->quadIndexBuffer()) {
1030 SkDebugf("Could not allocate buffers\n");
1031 return;
1032 }
1033
joshualitt76e7fb62015-02-11 08:52:27 -08001034 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1035
1036 for (int i = 0; i < instanceCount; i++) {
1037 Geometry& args = fGeoData[i];
1038
1039 SkScalar xRadius = args.fXRadius;
1040 SkScalar yRadius = args.fYRadius;
1041
1042 // Compute the reciprocals of the radii here to save time in the shader
1043 SkScalar xRadRecip = SkScalarInvert(xRadius);
1044 SkScalar yRadRecip = SkScalarInvert(yRadius);
1045 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1046 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1047
egdanielbc227142015-04-21 06:28:08 -07001048 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001049
1050 // The inner radius in the vertex data must be specified in normalized space.
1051 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1052 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
1053 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1054 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1055
1056 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1057 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
1058 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1059 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1060
1061 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1062 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
1063 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1064 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1065
1066 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1067 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
1068 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1069 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1070
1071 verts += kVertsPerEllipse;
1072 }
1073
1074 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1075
1076 GrDrawTarget::DrawInfo drawInfo;
1077 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1078 drawInfo.setStartVertex(0);
1079 drawInfo.setStartIndex(0);
1080 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1081 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1082 drawInfo.adjustStartVertex(firstVertex);
1083 drawInfo.setVertexBuffer(vertexBuffer);
1084 drawInfo.setIndexBuffer(quadIndexBuffer);
1085
1086 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1087
1088 while (instanceCount) {
1089 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1090 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1091 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1092
1093 batchTarget->draw(drawInfo);
1094
1095 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1096 instanceCount -= drawInfo.instanceCount();
1097 }
1098 }
1099
1100 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1101
1102private:
1103 EllipseBatch(const Geometry& geometry) {
1104 this->initClassID<EllipseBatch>();
1105 fGeoData.push_back(geometry);
1106 }
1107
mtklein36352bf2015-03-25 18:17:31 -07001108 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001109 EllipseBatch* that = t->cast<EllipseBatch>();
1110
1111 // TODO use vertex color to avoid breaking batches
1112 if (this->color() != that->color()) {
1113 return false;
1114 }
1115
1116 if (this->stroke() != that->stroke()) {
1117 return false;
1118 }
1119
1120 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1121 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1122 return false;
1123 }
1124
1125 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1126 return true;
1127 }
1128
1129 GrColor color() const { return fBatch.fColor; }
1130 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1131 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1132 bool stroke() const { return fBatch.fStroke; }
1133
1134 struct BatchTracker {
1135 GrColor fColor;
1136 bool fStroke;
1137 bool fUsesLocalCoords;
1138 bool fColorIgnored;
1139 bool fCoverageIgnored;
1140 };
1141
1142 static const int kVertsPerEllipse = 4;
1143 static const int kIndicesPerEllipse = 6;
1144
joshualitt76e7fb62015-02-11 08:52:27 -08001145 BatchTracker fBatch;
1146 SkSTArray<1, Geometry, true> fGeoData;
1147};
1148
joshualitt3e708c52015-04-30 13:49:27 -07001149static GrBatch* create_ellipse_batch(GrColor color,
1150 const SkMatrix& viewMatrix,
1151 bool useCoverageAA,
1152 const SkRect& ellipse,
1153 const SkStrokeRec& stroke,
1154 SkRect* bounds) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001155#ifdef SK_DEBUG
1156 {
1157 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001158 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001159 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001160 }
1161#endif
1162
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001163 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001164 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001165 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001166 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1167 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001168 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1169 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1170 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1171 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001172
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001173 // do (potentially) anisotropic mapping of stroke
1174 SkVector scaledStroke;
1175 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001176 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1177 viewMatrix[SkMatrix::kMSkewY]));
1178 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1179 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001180
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001181 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001182 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1183 SkStrokeRec::kHairline_Style == style;
1184 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001185
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001186 SkScalar innerXRadius = 0;
1187 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001188 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001189 if (SkScalarNearlyZero(scaledStroke.length())) {
1190 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1191 } else {
1192 scaledStroke.scale(SK_ScalarHalf);
1193 }
1194
1195 // we only handle thick strokes for near-circular ellipses
1196 if (scaledStroke.length() > SK_ScalarHalf &&
1197 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001198 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001199 }
1200
1201 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1202 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1203 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001204 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001205 }
1206
1207 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001208 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001209 innerXRadius = xRadius - scaledStroke.fX;
1210 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001211 }
1212
1213 xRadius += scaledStroke.fX;
1214 yRadius += scaledStroke.fY;
1215 }
1216
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001217 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001218 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001219 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001220 xRadius += SK_ScalarHalf;
1221 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001222
joshualitt3e708c52015-04-30 13:49:27 -07001223 bounds->setLTRB(center.fX - xRadius, center.fY - yRadius,
1224 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001225
joshualitt76e7fb62015-02-11 08:52:27 -08001226 EllipseBatch::Geometry geometry;
1227 geometry.fViewMatrix = viewMatrix;
1228 geometry.fColor = color;
1229 geometry.fXRadius = xRadius;
1230 geometry.fYRadius = yRadius;
1231 geometry.fInnerXRadius = innerXRadius;
1232 geometry.fInnerYRadius = innerYRadius;
1233 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualitt3e708c52015-04-30 13:49:27 -07001234 geometry.fDevBounds = *bounds;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001235
joshualitt3e708c52015-04-30 13:49:27 -07001236 return EllipseBatch::Create(geometry);
1237}
1238
1239bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
1240 GrPipelineBuilder* pipelineBuilder,
1241 GrColor color,
1242 const SkMatrix& viewMatrix,
1243 bool useCoverageAA,
1244 const SkRect& ellipse,
1245 const SkStrokeRec& stroke) {
1246 SkRect bounds;
1247 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1248 stroke, &bounds));
1249 if (!batch) {
1250 return false;
1251 }
1252
joshualitt76e7fb62015-02-11 08:52:27 -08001253 target->drawBatch(pipelineBuilder, batch, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001254 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001255}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001256
joshualitt76e7fb62015-02-11 08:52:27 -08001257/////////////////////////////////////////////////////////////////////////////////////////////////
1258
1259class DIEllipseBatch : public GrBatch {
1260public:
1261 struct Geometry {
1262 GrColor fColor;
1263 SkMatrix fViewMatrix;
1264 SkScalar fXRadius;
1265 SkScalar fYRadius;
1266 SkScalar fInnerXRadius;
1267 SkScalar fInnerYRadius;
1268 SkScalar fGeoDx;
1269 SkScalar fGeoDy;
1270 DIEllipseEdgeEffect::Mode fMode;
egdaniel9ef1bb12015-04-20 12:28:57 -07001271 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001272 };
1273
1274 static GrBatch* Create(const Geometry& geometry) {
1275 return SkNEW_ARGS(DIEllipseBatch, (geometry));
1276 }
1277
mtklein36352bf2015-03-25 18:17:31 -07001278 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001279
mtklein36352bf2015-03-25 18:17:31 -07001280 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001281 // When this is called on a batch, there is only one geometry bundle
1282 out->setKnownFourComponents(fGeoData[0].fColor);
1283 }
mtklein36352bf2015-03-25 18:17:31 -07001284 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001285 out->setUnknownSingleComponent();
1286 }
1287
mtklein36352bf2015-03-25 18:17:31 -07001288 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001289 // Handle any color overrides
1290 if (init.fColorIgnored) {
1291 fGeoData[0].fColor = GrColor_ILLEGAL;
1292 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1293 fGeoData[0].fColor = init.fOverrideColor;
1294 }
1295
1296 // setup batch properties
1297 fBatch.fColorIgnored = init.fColorIgnored;
1298 fBatch.fColor = fGeoData[0].fColor;
1299 fBatch.fMode = fGeoData[0].fMode;
1300 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1301 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1302 }
1303
mtklein36352bf2015-03-25 18:17:31 -07001304 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001305 // Setup geometry processor
1306 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1307 this->viewMatrix(),
1308 this->mode()));
1309
joshualitt76e7fb62015-02-11 08:52:27 -08001310 batchTarget->initDraw(gp, pipeline);
1311
1312 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1313 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1314 // everywhere we can remove this nastiness
1315 GrPipelineInfo init;
1316 init.fColorIgnored = fBatch.fColorIgnored;
1317 init.fOverrideColor = GrColor_ILLEGAL;
1318 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1319 init.fUsesLocalCoords = this->usesLocalCoords();
1320 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1321
1322 int instanceCount = fGeoData.count();
1323 int vertexCount = kVertsPerEllipse * instanceCount;
1324 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001325 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
joshualitt76e7fb62015-02-11 08:52:27 -08001326
1327 const GrVertexBuffer* vertexBuffer;
1328 int firstVertex;
1329
1330 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1331 vertexCount,
1332 &vertexBuffer,
1333 &firstVertex);
1334
joshualitt4b31de82015-03-05 14:33:41 -08001335 if (!vertices || !batchTarget->quadIndexBuffer()) {
1336 SkDebugf("Could not allocate buffers\n");
1337 return;
1338 }
1339
joshualitt76e7fb62015-02-11 08:52:27 -08001340 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(vertices);
1341
1342 for (int i = 0; i < instanceCount; i++) {
1343 Geometry& args = fGeoData[i];
1344
1345 SkScalar xRadius = args.fXRadius;
1346 SkScalar yRadius = args.fYRadius;
1347
egdaniel9ef1bb12015-04-20 12:28:57 -07001348 const SkRect& bounds = args.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001349
1350 // This adjusts the "radius" to include the half-pixel border
1351 SkScalar offsetDx = SkScalarDiv(args.fGeoDx, xRadius);
1352 SkScalar offsetDy = SkScalarDiv(args.fGeoDy, yRadius);
1353
1354 SkScalar innerRatioX = SkScalarDiv(xRadius, args.fInnerXRadius);
1355 SkScalar innerRatioY = SkScalarDiv(yRadius, args.fInnerYRadius);
1356
1357 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1358 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1359 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1360
1361 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1362 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1363 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1364
1365 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1366 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1367 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1368
1369 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1370 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1371 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1372
1373 verts += kVertsPerEllipse;
1374 }
1375
1376 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer();
1377
1378 GrDrawTarget::DrawInfo drawInfo;
1379 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1380 drawInfo.setStartVertex(0);
1381 drawInfo.setStartIndex(0);
1382 drawInfo.setVerticesPerInstance(kVertsPerEllipse);
1383 drawInfo.setIndicesPerInstance(kIndicesPerEllipse);
1384 drawInfo.adjustStartVertex(firstVertex);
1385 drawInfo.setVertexBuffer(vertexBuffer);
1386 drawInfo.setIndexBuffer(quadIndexBuffer);
1387
1388 int maxInstancesPerDraw = quadIndexBuffer->maxQuads();
1389
1390 while (instanceCount) {
1391 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1392 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1393 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1394
1395 batchTarget->draw(drawInfo);
1396
1397 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1398 instanceCount -= drawInfo.instanceCount();
1399 }
1400 }
1401
1402 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1403
1404private:
1405 DIEllipseBatch(const Geometry& geometry) {
1406 this->initClassID<DIEllipseBatch>();
1407 fGeoData.push_back(geometry);
1408 }
1409
mtklein36352bf2015-03-25 18:17:31 -07001410 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001411 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1412
1413 // TODO use vertex color to avoid breaking batches
1414 if (this->color() != that->color()) {
1415 return false;
1416 }
1417
1418 if (this->mode() != that->mode()) {
1419 return false;
1420 }
1421
1422 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1423 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1424 return false;
1425 }
1426
1427 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1428 return true;
1429 }
1430
1431 GrColor color() const { return fBatch.fColor; }
1432 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1433 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1434 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1435
1436 struct BatchTracker {
1437 GrColor fColor;
1438 DIEllipseEdgeEffect::Mode fMode;
1439 bool fUsesLocalCoords;
1440 bool fColorIgnored;
1441 bool fCoverageIgnored;
1442 };
1443
1444 static const int kVertsPerEllipse = 4;
1445 static const int kIndicesPerEllipse = 6;
1446
joshualitt76e7fb62015-02-11 08:52:27 -08001447 BatchTracker fBatch;
1448 SkSTArray<1, Geometry, true> fGeoData;
1449};
1450
joshualitt3e708c52015-04-30 13:49:27 -07001451static GrBatch* create_diellipse_batch(GrColor color,
1452 const SkMatrix& viewMatrix,
1453 bool useCoverageAA,
1454 const SkRect& ellipse,
1455 const SkStrokeRec& stroke,
1456 SkRect* bounds) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001457 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001458 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001459 SkScalar yRadius = SkScalarHalf(ellipse.height());
1460
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001461 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001462 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001463 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001464 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001465 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1466
1467 SkScalar innerXRadius = 0;
1468 SkScalar innerYRadius = 0;
1469 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1470 SkScalar strokeWidth = stroke.getWidth();
1471
1472 if (SkScalarNearlyZero(strokeWidth)) {
1473 strokeWidth = SK_ScalarHalf;
1474 } else {
1475 strokeWidth *= SK_ScalarHalf;
1476 }
1477
1478 // we only handle thick strokes for near-circular ellipses
1479 if (strokeWidth > SK_ScalarHalf &&
1480 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07001481 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001482 }
1483
1484 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1485 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1486 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07001487 return NULL;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001488 }
1489
1490 // set inner radius (if needed)
1491 if (SkStrokeRec::kStroke_Style == style) {
1492 innerXRadius = xRadius - strokeWidth;
1493 innerYRadius = yRadius - strokeWidth;
1494 }
1495
1496 xRadius += strokeWidth;
1497 yRadius += strokeWidth;
1498 }
1499 if (DIEllipseEdgeEffect::kStroke == mode) {
1500 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1501 DIEllipseEdgeEffect::kFill;
1502 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001503
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001504 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001505 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1506 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1507 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1508 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001509 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
1510 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001511
joshualitt3e708c52015-04-30 13:49:27 -07001512 bounds->setLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1513 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001514
joshualitt76e7fb62015-02-11 08:52:27 -08001515 DIEllipseBatch::Geometry geometry;
1516 geometry.fViewMatrix = viewMatrix;
1517 geometry.fColor = color;
1518 geometry.fXRadius = xRadius;
1519 geometry.fYRadius = yRadius;
1520 geometry.fInnerXRadius = innerXRadius;
1521 geometry.fInnerYRadius = innerYRadius;
1522 geometry.fGeoDx = geoDx;
1523 geometry.fGeoDy = geoDy;
1524 geometry.fMode = mode;
joshualitt3e708c52015-04-30 13:49:27 -07001525 geometry.fBounds = *bounds;
egdaniel9ef1bb12015-04-20 12:28:57 -07001526
joshualitt3e708c52015-04-30 13:49:27 -07001527 viewMatrix.mapRect(bounds);
1528 return DIEllipseBatch::Create(geometry);
1529}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001530
joshualitt3e708c52015-04-30 13:49:27 -07001531bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
1532 GrPipelineBuilder* pipelineBuilder,
1533 GrColor color,
1534 const SkMatrix& viewMatrix,
1535 bool useCoverageAA,
1536 const SkRect& ellipse,
1537 const SkStrokeRec& stroke) {
1538 SkRect bounds;
1539 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1540 stroke, &bounds));
1541 if (!batch) {
1542 return false;
1543 }
joshualitt76e7fb62015-02-11 08:52:27 -08001544 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001545 return true;
1546}
1547
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001548///////////////////////////////////////////////////////////////////////////////
1549
1550static const uint16_t gRRectIndices[] = {
1551 // corners
1552 0, 1, 5, 0, 5, 4,
1553 2, 3, 7, 2, 7, 6,
1554 8, 9, 13, 8, 13, 12,
1555 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001556
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001557 // edges
1558 1, 2, 6, 1, 6, 5,
1559 4, 5, 9, 4, 9, 8,
1560 6, 7, 11, 6, 11, 10,
1561 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001562
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001563 // center
1564 // we place this at the end so that we can ignore these indices when rendering stroke-only
1565 5, 6, 10, 5, 10, 9
1566};
1567
joshualitt5ead6da2014-10-22 16:00:29 -07001568static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1569static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1570static const int kVertsPerRRect = 16;
1571static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001572
joshualitt9853cce2014-11-17 14:22:48 -08001573bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
egdaniel8dd688b2015-01-22 10:16:09 -08001574 GrPipelineBuilder* pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001575 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001576 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001577 bool useAA,
1578 const SkRRect& origOuter,
1579 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001580 bool applyAA = useAA &&
egdaniel0bdeec92015-02-23 12:12:54 -08001581 !pipelineBuilder->getRenderTarget()->isMultisampled();
bsalomon6be6f7c2015-02-26 13:05:21 -08001582 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001583 if (!origInner.isEmpty()) {
1584 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001585 if (!viewMatrix.isIdentity()) {
1586 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001587 return false;
1588 }
1589 }
joshualittb0a8a372014-09-23 09:50:21 -07001590 GrPrimitiveEdgeType edgeType = applyAA ?
1591 kInverseFillAA_GrProcessorEdgeType :
1592 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001593 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001594 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1595 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001596 return false;
1597 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001598 arfp.set(pipelineBuilder);
egdaniel8dd688b2015-01-22 10:16:09 -08001599 pipelineBuilder->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001600 }
1601
1602 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
egdaniel8dd688b2015-01-22 10:16:09 -08001603 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001604 return true;
1605 }
1606
1607 SkASSERT(!origOuter.isEmpty());
1608 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001609 if (!viewMatrix.isIdentity()) {
1610 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001611 return false;
1612 }
1613 }
joshualittb0a8a372014-09-23 09:50:21 -07001614 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001615 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001616 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001617 if (NULL == effect) {
1618 return false;
1619 }
bsalomon6be6f7c2015-02-26 13:05:21 -08001620 if (!arfp.isSet()) {
1621 arfp.set(pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001622 }
joshualittd27f73e2014-12-29 07:43:36 -08001623
1624 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001625 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001626 return false;
1627 }
joshualittd27f73e2014-12-29 07:43:36 -08001628
egdaniel8dd688b2015-01-22 10:16:09 -08001629 pipelineBuilder->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001630 SkRect bounds = outer->getBounds();
1631 if (applyAA) {
1632 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1633 }
egdaniel8dd688b2015-01-22 10:16:09 -08001634 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001635 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001636}
1637
joshualitt76e7fb62015-02-11 08:52:27 -08001638///////////////////////////////////////////////////////////////////////////////////////////////////
1639
1640class RRectCircleRendererBatch : public GrBatch {
1641public:
1642 struct Geometry {
1643 GrColor fColor;
1644 SkMatrix fViewMatrix;
1645 SkScalar fInnerRadius;
1646 SkScalar fOuterRadius;
1647 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001648 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001649 };
1650
1651 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1652 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry, indexBuffer));
1653 }
1654
mtklein36352bf2015-03-25 18:17:31 -07001655 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001656
mtklein36352bf2015-03-25 18:17:31 -07001657 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001658 // When this is called on a batch, there is only one geometry bundle
1659 out->setKnownFourComponents(fGeoData[0].fColor);
1660 }
mtklein36352bf2015-03-25 18:17:31 -07001661 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001662 out->setUnknownSingleComponent();
1663 }
1664
mtklein36352bf2015-03-25 18:17:31 -07001665 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001666 // Handle any color overrides
1667 if (init.fColorIgnored) {
1668 fGeoData[0].fColor = GrColor_ILLEGAL;
1669 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1670 fGeoData[0].fColor = init.fOverrideColor;
1671 }
1672
1673 // setup batch properties
1674 fBatch.fColorIgnored = init.fColorIgnored;
1675 fBatch.fColor = fGeoData[0].fColor;
1676 fBatch.fStroke = fGeoData[0].fStroke;
1677 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1678 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1679 }
1680
mtklein36352bf2015-03-25 18:17:31 -07001681 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001682 // reset to device coordinates
1683 SkMatrix invert;
1684 if (!this->viewMatrix().invert(&invert)) {
1685 SkDebugf("Failed to invert\n");
1686 return;
1687 }
1688
1689 // Setup geometry processor
1690 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1691 this->stroke(),
1692 invert));
1693
1694 batchTarget->initDraw(gp, pipeline);
1695
1696 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1697 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1698 // everywhere we can remove this nastiness
1699 GrPipelineInfo init;
1700 init.fColorIgnored = fBatch.fColorIgnored;
1701 init.fOverrideColor = GrColor_ILLEGAL;
1702 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1703 init.fUsesLocalCoords = this->usesLocalCoords();
1704 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1705
1706 int instanceCount = fGeoData.count();
1707 int vertexCount = kVertsPerRRect * instanceCount;
1708 size_t vertexStride = gp->getVertexStride();
1709 SkASSERT(vertexStride == sizeof(CircleVertex));
1710
1711 const GrVertexBuffer* vertexBuffer;
1712 int firstVertex;
1713
1714 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1715 vertexCount,
1716 &vertexBuffer,
1717 &firstVertex);
1718
joshualitt4b31de82015-03-05 14:33:41 -08001719 if (!vertices) {
1720 SkDebugf("Could not allocate vertices\n");
1721 return;
1722 }
1723
joshualitt76e7fb62015-02-11 08:52:27 -08001724 CircleVertex* verts = reinterpret_cast<CircleVertex*>(vertices);
1725
1726 for (int i = 0; i < instanceCount; i++) {
1727 Geometry& args = fGeoData[i];
1728
1729 SkScalar outerRadius = args.fOuterRadius;
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 + outerRadius,
1736 bounds.fBottom - outerRadius,
1737 bounds.fBottom
1738 };
1739
1740 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1741 // The inner radius in the vertex data must be specified in normalized space.
1742 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1743 for (int i = 0; i < 4; ++i) {
1744 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1745 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1746 verts->fOuterRadius = outerRadius;
1747 verts->fInnerRadius = innerRadius;
1748 verts++;
1749
1750 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1751 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1752 verts->fOuterRadius = outerRadius;
1753 verts->fInnerRadius = innerRadius;
1754 verts++;
1755
1756 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1757 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1758 verts->fOuterRadius = outerRadius;
1759 verts->fInnerRadius = innerRadius;
1760 verts++;
1761
1762 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1763 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1764 verts->fOuterRadius = outerRadius;
1765 verts->fInnerRadius = innerRadius;
1766 verts++;
1767 }
1768 }
1769
1770 // drop out the middle quad if we're stroked
1771 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1772 SK_ARRAY_COUNT(gRRectIndices);
1773
1774
1775 GrDrawTarget::DrawInfo drawInfo;
1776 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1777 drawInfo.setStartVertex(0);
1778 drawInfo.setStartIndex(0);
1779 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1780 drawInfo.setIndicesPerInstance(indexCnt);
1781 drawInfo.adjustStartVertex(firstVertex);
1782 drawInfo.setVertexBuffer(vertexBuffer);
1783 drawInfo.setIndexBuffer(fIndexBuffer);
1784
1785 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
1786
1787 while (instanceCount) {
1788 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
1789 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
1790 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
1791
1792 batchTarget->draw(drawInfo);
1793
1794 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
1795 instanceCount -= drawInfo.instanceCount();
1796 }
1797 }
1798
1799 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1800
1801private:
1802 RRectCircleRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
1803 : fIndexBuffer(indexBuffer) {
1804 this->initClassID<RRectCircleRendererBatch>();
1805 fGeoData.push_back(geometry);
1806 }
1807
mtklein36352bf2015-03-25 18:17:31 -07001808 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001809 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1810
1811 // TODO use vertex color to avoid breaking batches
1812 if (this->color() != that->color()) {
1813 return false;
1814 }
1815
1816 if (this->stroke() != that->stroke()) {
1817 return false;
1818 }
1819
1820 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1821 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1822 return false;
1823 }
1824
1825 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
1826 return true;
1827 }
1828
1829 GrColor color() const { return fBatch.fColor; }
1830 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1831 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1832 bool stroke() const { return fBatch.fStroke; }
1833
1834 struct BatchTracker {
1835 GrColor fColor;
1836 bool fStroke;
1837 bool fUsesLocalCoords;
1838 bool fColorIgnored;
1839 bool fCoverageIgnored;
1840 };
1841
joshualitt76e7fb62015-02-11 08:52:27 -08001842 BatchTracker fBatch;
1843 SkSTArray<1, Geometry, true> fGeoData;
1844 const GrIndexBuffer* fIndexBuffer;
1845};
1846
1847class RRectEllipseRendererBatch : public GrBatch {
1848public:
1849 struct Geometry {
1850 GrColor fColor;
1851 SkMatrix fViewMatrix;
1852 SkScalar fXRadius;
1853 SkScalar fYRadius;
1854 SkScalar fInnerXRadius;
1855 SkScalar fInnerYRadius;
1856 bool fStroke;
egdanielbc227142015-04-21 06:28:08 -07001857 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001858 };
1859
1860 static GrBatch* Create(const Geometry& geometry, const GrIndexBuffer* indexBuffer) {
1861 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry, indexBuffer));
1862 }
1863
mtklein36352bf2015-03-25 18:17:31 -07001864 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001865
mtklein36352bf2015-03-25 18:17:31 -07001866 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001867 // When this is called on a batch, there is only one geometry bundle
1868 out->setKnownFourComponents(fGeoData[0].fColor);
1869 }
mtklein36352bf2015-03-25 18:17:31 -07001870 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001871 out->setUnknownSingleComponent();
1872 }
1873
mtklein36352bf2015-03-25 18:17:31 -07001874 void initBatchTracker(const GrPipelineInfo& init) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001875 // Handle any color overrides
1876 if (init.fColorIgnored) {
1877 fGeoData[0].fColor = GrColor_ILLEGAL;
1878 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
1879 fGeoData[0].fColor = init.fOverrideColor;
1880 }
1881
1882 // setup batch properties
1883 fBatch.fColorIgnored = init.fColorIgnored;
1884 fBatch.fColor = fGeoData[0].fColor;
1885 fBatch.fStroke = fGeoData[0].fStroke;
1886 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
1887 fBatch.fCoverageIgnored = init.fCoverageIgnored;
1888 }
1889
mtklein36352bf2015-03-25 18:17:31 -07001890 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001891 // reset to device coordinates
1892 SkMatrix invert;
1893 if (!this->viewMatrix().invert(&invert)) {
1894 SkDebugf("Failed to invert\n");
1895 return;
1896 }
1897
1898 // Setup geometry processor
1899 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1900 this->stroke(),
1901 invert));
1902
1903 batchTarget->initDraw(gp, pipeline);
1904
1905 // TODO this is hacky, but the only way we have to initialize the GP is to use the
1906 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch
1907 // everywhere we can remove this nastiness
1908 GrPipelineInfo init;
1909 init.fColorIgnored = fBatch.fColorIgnored;
1910 init.fOverrideColor = GrColor_ILLEGAL;
1911 init.fCoverageIgnored = fBatch.fCoverageIgnored;
1912 init.fUsesLocalCoords = this->usesLocalCoords();
1913 gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
1914
1915 int instanceCount = fGeoData.count();
1916 int vertexCount = kVertsPerRRect * instanceCount;
1917 size_t vertexStride = gp->getVertexStride();
1918 SkASSERT(vertexStride == sizeof(EllipseVertex));
1919
1920 const GrVertexBuffer* vertexBuffer;
1921 int firstVertex;
1922
1923 void *vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
1924 vertexCount,
1925 &vertexBuffer,
1926 &firstVertex);
1927
joshualitt4b31de82015-03-05 14:33:41 -08001928 if (!vertices) {
1929 SkDebugf("Could not allocate vertices\n");
1930 return;
1931 }
1932
joshualitt76e7fb62015-02-11 08:52:27 -08001933 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(vertices);
1934
1935 for (int i = 0; i < instanceCount; i++) {
1936 Geometry& args = fGeoData[i];
1937
1938 // Compute the reciprocals of the radii here to save time in the shader
1939 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1940 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1941 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1942 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1943
1944 // Extend the radii out half a pixel to antialias.
1945 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1946 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1947
egdanielbc227142015-04-21 06:28:08 -07001948 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001949
1950 SkScalar yCoords[4] = {
1951 bounds.fTop,
1952 bounds.fTop + yOuterRadius,
1953 bounds.fBottom - yOuterRadius,
1954 bounds.fBottom
1955 };
1956 SkScalar yOuterOffsets[4] = {
1957 yOuterRadius,
1958 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1959 SK_ScalarNearlyZero,
1960 yOuterRadius
1961 };
1962
1963 for (int i = 0; i < 4; ++i) {
1964 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1965 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1966 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1967 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1968 verts++;
1969
1970 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1971 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1972 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1973 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1974 verts++;
1975
1976 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1977 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1978 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1979 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1980 verts++;
1981
1982 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1983 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1984 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1985 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1986 verts++;
1987 }
1988 }
1989
1990 // drop out the middle quad if we're stroked
1991 int indexCnt = this->stroke() ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1992 SK_ARRAY_COUNT(gRRectIndices);
1993
1994 GrDrawTarget::DrawInfo drawInfo;
1995 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
1996 drawInfo.setStartVertex(0);
1997 drawInfo.setStartIndex(0);
1998 drawInfo.setVerticesPerInstance(kVertsPerRRect);
1999 drawInfo.setIndicesPerInstance(indexCnt);
2000 drawInfo.adjustStartVertex(firstVertex);
2001 drawInfo.setVertexBuffer(vertexBuffer);
2002 drawInfo.setIndexBuffer(fIndexBuffer);
2003
2004 int maxInstancesPerDraw = kNumRRectsInIndexBuffer;
2005
2006 while (instanceCount) {
2007 drawInfo.setInstanceCount(SkTMin(instanceCount, maxInstancesPerDraw));
2008 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.verticesPerInstance());
2009 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPerInstance());
2010
2011 batchTarget->draw(drawInfo);
2012
2013 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCount());
2014 instanceCount -= drawInfo.instanceCount();
2015 }
2016 }
2017
2018 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
2019
2020private:
2021 RRectEllipseRendererBatch(const Geometry& geometry, const GrIndexBuffer* indexBuffer)
2022 : fIndexBuffer(indexBuffer) {
2023 this->initClassID<RRectEllipseRendererBatch>();
2024 fGeoData.push_back(geometry);
2025 }
2026
mtklein36352bf2015-03-25 18:17:31 -07002027 bool onCombineIfPossible(GrBatch* t) override {
joshualitt76e7fb62015-02-11 08:52:27 -08002028 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2029
2030 // TODO use vertex color to avoid breaking batches
2031 if (this->color() != that->color()) {
2032 return false;
2033 }
2034
2035 if (this->stroke() != that->stroke()) {
2036 return false;
2037 }
2038
2039 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
2040 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
2041 return false;
2042 }
2043
2044 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
2045 return true;
2046 }
2047
2048 GrColor color() const { return fBatch.fColor; }
2049 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
2050 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
2051 bool stroke() const { return fBatch.fStroke; }
2052
2053 struct BatchTracker {
2054 GrColor fColor;
2055 bool fStroke;
2056 bool fUsesLocalCoords;
2057 bool fColorIgnored;
2058 bool fCoverageIgnored;
2059 };
2060
joshualitt76e7fb62015-02-11 08:52:27 -08002061 BatchTracker fBatch;
2062 SkSTArray<1, Geometry, true> fGeoData;
2063 const GrIndexBuffer* fIndexBuffer;
2064};
2065
joshualitt3e708c52015-04-30 13:49:27 -07002066static GrIndexBuffer* create_rrect_indexbuffer(GrIndexBuffer** strokeRRectIndexBuffer,
2067 GrIndexBuffer** rrectIndexBuffer,
2068 bool isStrokeOnly,
2069 GrGpu* gpu) {
2070 if (isStrokeOnly) {
2071 if (NULL == *strokeRRectIndexBuffer) {
2072 *strokeRRectIndexBuffer = gpu->createInstancedIndexBuffer(gRRectIndices,
2073 kIndicesPerStrokeRRect,
2074 kNumRRectsInIndexBuffer,
2075 kVertsPerRRect);
2076 }
2077 return *strokeRRectIndexBuffer;
2078 } else {
2079 if (NULL == *rrectIndexBuffer) {
2080 *rrectIndexBuffer = gpu->createInstancedIndexBuffer(gRRectIndices,
2081 kIndicesPerRRect,
2082 kNumRRectsInIndexBuffer,
2083 kVertsPerRRect);
2084 }
2085 return *rrectIndexBuffer;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002086 }
joshualitt3e708c52015-04-30 13:49:27 -07002087}
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002088
joshualitt3e708c52015-04-30 13:49:27 -07002089static GrBatch* create_rrect_batch(GrColor color,
2090 const SkMatrix& viewMatrix,
2091 const SkRRect& rrect,
2092 const SkStrokeRec& stroke,
2093 SkRect* bounds,
2094 GrIndexBuffer** strokeRRectIndexBuffer,
2095 GrIndexBuffer** rrectIndexBuffer,
2096 GrGpu* gpu) {
2097 SkASSERT(viewMatrix.rectStaysRect());
2098 SkASSERT(rrect.isSimple());
2099 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002100
joshualitt3e708c52015-04-30 13:49:27 -07002101 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002102 // do any matrix crunching before we reset the draw state for device coords
2103 const SkRect& rrectBounds = rrect.getBounds();
joshualitt3e708c52015-04-30 13:49:27 -07002104 viewMatrix.mapRect(bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002105
2106 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002107 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2108 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2109 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2110 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002111
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002112 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002113
2114 // do (potentially) anisotropic mapping of stroke
2115 SkVector scaledStroke;
2116 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002117
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002118 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2119 SkStrokeRec::kHairline_Style == style;
2120 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2121
2122 if (hasStroke) {
2123 if (SkStrokeRec::kHairline_Style == style) {
2124 scaledStroke.set(1, 1);
2125 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002126 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2127 viewMatrix[SkMatrix::kMSkewY]));
2128 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2129 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002130 }
2131
2132 // if half of strokewidth is greater than radius, we don't handle that right now
2133 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002134 return NULL;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002135 }
2136 }
2137
2138 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2139 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2140 // patch will have fractional coverage. This only matters when the interior is actually filled.
2141 // We could consider falling back to rect rendering here, since a tiny radius is
2142 // indistinguishable from a square corner.
2143 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002144 return NULL;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002145 }
2146
joshualitt3e708c52015-04-30 13:49:27 -07002147 GrIndexBuffer* indexBuffer = create_rrect_indexbuffer(strokeRRectIndexBuffer,
2148 rrectIndexBuffer,
2149 isStrokeOnly,
2150 gpu);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002151 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -07002152 SkDebugf("Failed to create index buffer!\n");
joshualitt3e708c52015-04-30 13:49:27 -07002153 return NULL;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002154 }
2155
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002156 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002157 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002158 SkScalar innerRadius = 0.0f;
2159 SkScalar outerRadius = xRadius;
2160 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002161 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002162 if (SkScalarNearlyZero(scaledStroke.fX)) {
2163 halfWidth = SK_ScalarHalf;
2164 } else {
2165 halfWidth = SkScalarHalf(scaledStroke.fX);
2166 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002167
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002168 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002169 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002170 }
2171 outerRadius += halfWidth;
joshualitt3e708c52015-04-30 13:49:27 -07002172 bounds->outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002173 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002174
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002175 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002176
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002177 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08002178 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
2179 // Second, the outer radius is used to compute the verts of the bounding box that is
2180 // rendered and the outset ensures the box will cover all partially covered by the rrect
2181 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002182 outerRadius += SK_ScalarHalf;
2183 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002184
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002185 // Expand the rect so all the pixels will be captured.
joshualitt3e708c52015-04-30 13:49:27 -07002186 bounds->outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002187
joshualitt76e7fb62015-02-11 08:52:27 -08002188 RRectCircleRendererBatch::Geometry geometry;
2189 geometry.fViewMatrix = viewMatrix;
2190 geometry.fColor = color;
2191 geometry.fInnerRadius = innerRadius;
2192 geometry.fOuterRadius = outerRadius;
2193 geometry.fStroke = isStrokeOnly;
joshualitt3e708c52015-04-30 13:49:27 -07002194 geometry.fDevBounds = *bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002195
joshualitt3e708c52015-04-30 13:49:27 -07002196 return RRectCircleRendererBatch::Create(geometry, indexBuffer);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00002197
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002198 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002199 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002200 SkScalar innerXRadius = 0.0f;
2201 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002202 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002203 if (SkScalarNearlyZero(scaledStroke.length())) {
2204 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
2205 } else {
2206 scaledStroke.scale(SK_ScalarHalf);
2207 }
2208
2209 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00002210 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002211 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
joshualitt3e708c52015-04-30 13:49:27 -07002212 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002213 }
2214
2215 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2216 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
2217 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
joshualitt3e708c52015-04-30 13:49:27 -07002218 return NULL;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002219 }
2220
2221 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002222 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002223 innerXRadius = xRadius - scaledStroke.fX;
2224 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002225 }
2226
2227 xRadius += scaledStroke.fX;
2228 yRadius += scaledStroke.fY;
joshualitt3e708c52015-04-30 13:49:27 -07002229 bounds->outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002230 }
jvanverth@google.come3647412013-05-08 15:31:43 +00002231
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002232 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00002233
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002234 // Expand the rect so all the pixels will be captured.
joshualitt3e708c52015-04-30 13:49:27 -07002235 bounds->outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002236
joshualitt76e7fb62015-02-11 08:52:27 -08002237 RRectEllipseRendererBatch::Geometry geometry;
2238 geometry.fViewMatrix = viewMatrix;
2239 geometry.fColor = color;
2240 geometry.fXRadius = xRadius;
2241 geometry.fYRadius = yRadius;
2242 geometry.fInnerXRadius = innerXRadius;
2243 geometry.fInnerYRadius = innerYRadius;
2244 geometry.fStroke = isStrokeOnly;
joshualitt3e708c52015-04-30 13:49:27 -07002245 geometry.fDevBounds = *bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002246
joshualitt3e708c52015-04-30 13:49:27 -07002247 return RRectEllipseRendererBatch::Create(geometry, indexBuffer);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002248 }
joshualitt3e708c52015-04-30 13:49:27 -07002249}
2250
2251bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
2252 GrPipelineBuilder* pipelineBuilder,
2253 GrColor color,
2254 const SkMatrix& viewMatrix,
2255 bool useAA,
2256 const SkRRect& rrect,
2257 const SkStrokeRec& stroke) {
2258 if (rrect.isOval()) {
2259 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2260 stroke);
2261 }
2262
2263 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled();
2264
2265 // only anti-aliased rrects for now
2266 if (!useCoverageAA) {
2267 return false;
2268 }
2269
2270 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2271 return false;
2272 }
2273
2274 SkRect bounds;
2275 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke, &bounds,
2276 &fStrokeRRectIndexBuffer, &fRRectIndexBuffer,
2277 fGpu));
2278 if (!batch) {
2279 return false;
2280 }
2281
2282 target->drawBatch(pipelineBuilder, batch, &bounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002283 return true;
2284}
joshualitt3e708c52015-04-30 13:49:27 -07002285
2286///////////////////////////////////////////////////////////////////////////////////////////////////
2287
2288#ifdef GR_TEST_UTILS
2289
2290static SkStrokeRec random_strokerec(SkRandom* random) {
2291 SkStrokeRec::InitStyle style =
2292 SkStrokeRec::InitStyle(random->nextULessThan(SkStrokeRec::kFill_InitStyle + 1));
2293 SkStrokeRec rec(style);
2294 bool strokeAndFill = random->nextBool();
2295 SkScalar strokeWidth = random->nextBool() ? 0.f : 1.f;
2296 rec.setStrokeStyle(strokeWidth, strokeAndFill);
2297 return rec;
2298}
2299
2300BATCH_TEST_DEFINE(CircleBatch) {
2301 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2302 GrColor color = GrRandomColor(random);
2303 bool useCoverageAA = random->nextBool();
2304 SkRect circle = GrTest::TestRect(random);
2305 SkRect bounds; // unused
2306 return create_circle_batch(color, viewMatrix, useCoverageAA, circle, random_strokerec(random),
2307 &bounds);
2308}
2309
2310BATCH_TEST_DEFINE(EllipseBatch) {
2311 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2312 GrColor color = GrRandomColor(random);
2313 bool useCoverageAA = random->nextBool();
2314 SkRect ellipse = GrTest::TestRect(random);
2315 SkRect bounds; // unused
2316 return create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2317 random_strokerec(random), &bounds);
2318}
2319
2320BATCH_TEST_DEFINE(DIEllipseBatch) {
2321 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2322 GrColor color = GrRandomColor(random);
2323 bool useCoverageAA = random->nextBool();
2324 SkRect ellipse = GrTest::TestRect(random);
2325 SkRect bounds; // unused
2326 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
2327 random_strokerec(random), &bounds);
2328}
2329
2330BATCH_TEST_DEFINE(RRectBatch) {
2331 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2332 GrColor color = GrRandomColor(random);
2333 const SkRRect& rrect = GrTest::TestRRectSimple(random);
2334
2335 static GrIndexBuffer* gStrokeRRectIndexBuffer;
2336 static GrIndexBuffer* gRRectIndexBuffer;
2337 SkRect bounds;
2338 return create_rrect_batch(color, viewMatrix, rrect, random_strokerec(random), &bounds,
2339 &gStrokeRRectIndexBuffer, &gRRectIndexBuffer, context->getGpu());
2340}
2341
2342#endif