blob: 7f93bbe33916a0048ffbb4c9ba18463668c2ba16 [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
joshualittb0a8a372014-09-23 09:50:21 -070010#include "GrProcessor.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000011#include "GrDrawState.h"
12#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080013#include "GrGeometryProcessor.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000014#include "GrGpu.h"
egdaniel605dd0f2014-11-12 08:35:25 -080015#include "GrInvariantOutput.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000018#include "SkTLazy.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000019#include "effects/GrRRectEffect.h"
joshualitteb2a6762014-12-04 11:35:33 -080020#include "gl/GrGLProcessor.h"
21#include "gl/GrGLSL.h"
22#include "gl/GrGLGeometryProcessor.h"
23#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000024
commit-bot@chromium.org81312832013-03-22 18:34:09 +000025namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070026// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000027struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000028 SkPoint fPos;
29 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030 SkScalar fOuterRadius;
31 SkScalar fInnerRadius;
32};
33
34struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fPos;
36 SkPoint fOffset;
37 SkPoint fOuterRadii;
38 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000039};
40
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000041struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPos;
43 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
58 * with origin at the circle center. Two vertex attributes are used:
59 * vec2f : position in device space of the bounding geometry vertices
60 * vec4f : (p.xy, outerRad, innerRad)
61 * p is the position in the normalized space.
62 * outerRad is the outerRadius in device space.
63 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000064 */
65
joshualitt249af152014-09-15 11:41:13 -070066class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000067public:
joshualittd27f73e2014-12-29 07:43:36 -080068 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
69 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070 }
71
joshualitt2dd1ae02014-12-03 06:24:10 -080072 const GrAttribute* inPosition() const { return fInPosition; }
73 const GrAttribute* inCircleEdge() const { return fInCircleEdge; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000074 virtual ~CircleEdgeEffect() {}
75
mtklein72c9faa2015-01-09 10:06:39 -080076 const char* name() const SK_OVERRIDE { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077
78 inline bool isStroked() const { return fStroke; }
79
joshualittb0a8a372014-09-23 09:50:21 -070080 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000081 public:
joshualitteb2a6762014-12-04 11:35:33 -080082 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -080083 const GrBatchTracker&)
84 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000085
mtklein72c9faa2015-01-09 10:06:39 -080086 void emitCode(const EmitArgs& args) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -080087 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080088 GrGLGPBuilder* pb = args.fPB;
89 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -080090 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
91
joshualitt74077b92014-10-24 11:26:03 -070092 GrGLVertToFrag v(kVec4f_GrSLType);
93 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -080094 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095
joshualitt9b989322014-12-15 14:16:27 -080096 // Setup pass through color
97 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
98 &fColorUniform);
99
joshualitt2dd1ae02014-12-03 06:24:10 -0800100 // setup coord outputs
101 vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ce.inPosition()->fName);
102 vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ce.inPosition()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000103
joshualittee2af952014-12-30 09:04:15 -0800104 // setup uniform viewMatrix
105 this->addUniformViewMatrix(pb);
106
joshualitt4973d9d2014-11-08 09:24:25 -0800107 // setup position varying
108 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
joshualittee2af952014-12-30 09:04:15 -0800109 this->uViewM(), ce.inPosition()->fName);
joshualitt4973d9d2014-11-08 09:24:25 -0800110
joshualittc369e7c2014-10-22 10:56:26 -0700111 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700112 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800113 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800114 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800115 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
116 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700117 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000118 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000119
joshualitt2dd1ae02014-12-03 06:24:10 -0800120 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000121 }
122
joshualitt87f48d92014-12-04 10:41:40 -0800123 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800124 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800125 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700126 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800127 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700128 const CircleEdgeEffect& circleEffect = processor.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800129 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
130 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
131 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000132 }
133
joshualitt9b989322014-12-15 14:16:27 -0800134 virtual void setData(const GrGLProgramDataManager& pdman,
135 const GrPrimitiveProcessor& gp,
136 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800137 this->setUniformViewMatrix(pdman, gp.viewMatrix());
138
joshualitt9b989322014-12-15 14:16:27 -0800139 const BatchTracker& local = bt.cast<BatchTracker>();
140 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
141 GrGLfloat c[4];
142 GrColorToRGBAFloat(local.fColor, c);
143 pdman.set4fv(fColorUniform, 1, c);
144 fColor = local.fColor;
145 }
146 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000147
148 private:
joshualitt9b989322014-12-15 14:16:27 -0800149 GrColor fColor;
150 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700151 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152 };
153
joshualitteb2a6762014-12-04 11:35:33 -0800154 virtual void getGLProcessorKey(const GrBatchTracker& bt,
155 const GrGLCaps& caps,
156 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
157 GLProcessor::GenKey(*this, bt, caps, b);
158 }
159
mtklein72c9faa2015-01-09 10:06:39 -0800160 GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800161 return SkNEW_ARGS(GLProcessor, (*this, bt));
162 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000163
joshualitt9b989322014-12-15 14:16:27 -0800164 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
165 BatchTracker* local = bt->cast<BatchTracker>();
166 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800167 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800168 }
169
joshualitt290c09b2014-12-19 13:45:20 -0800170 bool onCanMakeEqual(const GrBatchTracker& m,
171 const GrGeometryProcessor& that,
172 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800173 const BatchTracker& mine = m.cast<BatchTracker>();
174 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800175 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
176 that, theirs.fUsesLocalCoords) &&
177 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800178 theirs.fInputColorType, theirs.fColor);
179 }
180
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000181private:
joshualittd27f73e2014-12-29 07:43:36 -0800182 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800183 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800184 this->initClassID<CircleEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800185 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
186 fInCircleEdge = &this->addVertexAttrib(GrAttribute("inCircleEdge",
187 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000188 fStroke = stroke;
189 }
190
mtklein72c9faa2015-01-09 10:06:39 -0800191 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700192 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000193 return cee.fStroke == fStroke;
194 }
195
mtklein72c9faa2015-01-09 10:06:39 -0800196 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800197 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700198 }
199
joshualitt9b989322014-12-15 14:16:27 -0800200 struct BatchTracker {
201 GrGPInput fInputColorType;
202 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800203 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800204 };
205
joshualitt2dd1ae02014-12-03 06:24:10 -0800206 const GrAttribute* fInPosition;
207 const GrAttribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208 bool fStroke;
209
joshualittb0a8a372014-09-23 09:50:21 -0700210 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211
joshualitt249af152014-09-15 11:41:13 -0700212 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000213};
214
joshualittb0a8a372014-09-23 09:50:21 -0700215GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216
joshualittb0a8a372014-09-23 09:50:21 -0700217GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
218 GrContext* context,
219 const GrDrawTargetCaps&,
220 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800221 return CircleEdgeEffect::Create(GrRandomColor(random),
222 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800223 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224}
225
226///////////////////////////////////////////////////////////////////////////////
227
228/**
229 * 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 +0000230 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
231 * in both x and y directions.
232 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000233 * 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 +0000234 */
235
joshualitt249af152014-09-15 11:41:13 -0700236class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237public:
joshualittd27f73e2014-12-29 07:43:36 -0800238 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
239 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000240 }
241
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242 virtual ~EllipseEdgeEffect() {}
243
mtklein72c9faa2015-01-09 10:06:39 -0800244 const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800245
246 const GrAttribute* inPosition() const { return fInPosition; }
247 const GrAttribute* inEllipseOffset() const { return fInEllipseOffset; }
248 const GrAttribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700249
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250 inline bool isStroked() const { return fStroke; }
251
joshualittb0a8a372014-09-23 09:50:21 -0700252 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 public:
joshualitteb2a6762014-12-04 11:35:33 -0800254 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800255 const GrBatchTracker&)
256 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000257
mtklein72c9faa2015-01-09 10:06:39 -0800258 void emitCode(const EmitArgs& args) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -0800259 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800260 GrGLGPBuilder* pb = args.fPB;
261 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800262 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000263
joshualitt74077b92014-10-24 11:26:03 -0700264 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
265 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700266 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800267 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
joshualitt74077b92014-10-24 11:26:03 -0700269 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
270 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
271 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800272 ee.inEllipseRadii()->fName);
273
joshualitt9b989322014-12-15 14:16:27 -0800274 // Setup pass through color
275 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
276 &fColorUniform);
277
joshualitt2dd1ae02014-12-03 06:24:10 -0800278 // setup coord outputs
279 vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ee.inPosition()->fName);
280 vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ee.inPosition()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000281
joshualittee2af952014-12-30 09:04:15 -0800282 // setup uniform viewMatrix
283 this->addUniformViewMatrix(pb);
284
joshualitt4973d9d2014-11-08 09:24:25 -0800285 // setup position varying
286 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
joshualittee2af952014-12-30 09:04:15 -0800287 this->uViewM(), ee.inPosition()->fName);
joshualitt4973d9d2014-11-08 09:24:25 -0800288
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000289 // for outer curve
joshualittc369e7c2014-10-22 10:56:26 -0700290 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700291 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
292 ellipseRadii.fsIn());
293 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
294 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
295 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
296
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000297 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700298 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
299 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
300 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000301
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000302 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800303 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700304 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
305 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
306 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
307 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
308 ellipseRadii.fsIn());
309 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
310 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000311 }
312
joshualitt2dd1ae02014-12-03 06:24:10 -0800313 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314 }
315
joshualitt87f48d92014-12-04 10:41:40 -0800316 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800317 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800318 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700319 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800320 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700321 const EllipseEdgeEffect& ellipseEffect = processor.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800322 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
323 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
324 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000325 }
326
joshualitt9b989322014-12-15 14:16:27 -0800327 virtual void setData(const GrGLProgramDataManager& pdman,
328 const GrPrimitiveProcessor& gp,
329 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800330 this->setUniformViewMatrix(pdman, gp.viewMatrix());
331
joshualitt9b989322014-12-15 14:16:27 -0800332 const BatchTracker& local = bt.cast<BatchTracker>();
333 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
334 GrGLfloat c[4];
335 GrColorToRGBAFloat(local.fColor, c);
336 pdman.set4fv(fColorUniform, 1, c);
337 fColor = local.fColor;
338 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000339 }
340
341 private:
joshualitt9b989322014-12-15 14:16:27 -0800342 GrColor fColor;
343 UniformHandle fColorUniform;
344
joshualitt249af152014-09-15 11:41:13 -0700345 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 };
347
joshualitteb2a6762014-12-04 11:35:33 -0800348 virtual void getGLProcessorKey(const GrBatchTracker& bt,
349 const GrGLCaps& caps,
350 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
351 GLProcessor::GenKey(*this, bt, caps, b);
352 }
353
mtklein72c9faa2015-01-09 10:06:39 -0800354 GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800355 return SkNEW_ARGS(GLProcessor, (*this, bt));
356 }
357
joshualitt9b989322014-12-15 14:16:27 -0800358 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
359 BatchTracker* local = bt->cast<BatchTracker>();
360 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800361 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800362 }
363
joshualitt290c09b2014-12-19 13:45:20 -0800364 bool onCanMakeEqual(const GrBatchTracker& m,
365 const GrGeometryProcessor& that,
366 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800367 const BatchTracker& mine = m.cast<BatchTracker>();
368 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800369 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
370 that, theirs.fUsesLocalCoords) &&
371 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800372 theirs.fInputColorType, theirs.fColor);
373 }
374
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000375private:
joshualittd27f73e2014-12-29 07:43:36 -0800376 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800377 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800378 this->initClassID<EllipseEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800379 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
380 fInEllipseOffset = &this->addVertexAttrib(GrAttribute("inEllipseOffset",
381 kVec2f_GrVertexAttribType));
382 fInEllipseRadii = &this->addVertexAttrib(GrAttribute("inEllipseRadii",
383 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000384 fStroke = stroke;
385 }
386
mtklein72c9faa2015-01-09 10:06:39 -0800387 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700388 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000389 return eee.fStroke == fStroke;
390 }
391
mtklein72c9faa2015-01-09 10:06:39 -0800392 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800393 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700394 }
395
joshualitt9b989322014-12-15 14:16:27 -0800396 struct BatchTracker {
397 GrGPInput fInputColorType;
398 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800399 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800400 };
401
joshualitt2dd1ae02014-12-03 06:24:10 -0800402 const GrAttribute* fInPosition;
403 const GrAttribute* fInEllipseOffset;
404 const GrAttribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000405 bool fStroke;
406
joshualittb0a8a372014-09-23 09:50:21 -0700407 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000408
joshualitt249af152014-09-15 11:41:13 -0700409 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000410};
411
joshualittb0a8a372014-09-23 09:50:21 -0700412GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000413
joshualittb0a8a372014-09-23 09:50:21 -0700414GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
415 GrContext* context,
416 const GrDrawTargetCaps&,
417 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800418 return EllipseEdgeEffect::Create(GrRandomColor(random),
419 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800420 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000421}
422
423///////////////////////////////////////////////////////////////////////////////
424
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000425/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000426 * 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 +0000427 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
428 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
429 * using differentials.
430 *
431 * The result is device-independent and can be used with any affine matrix.
432 */
433
joshualitt249af152014-09-15 11:41:13 -0700434class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000435public:
436 enum Mode { kStroke = 0, kHairline, kFill };
437
joshualitt8059eb92014-12-29 15:10:07 -0800438 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
439 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000440 }
441
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000442 virtual ~DIEllipseEdgeEffect() {}
443
mtklein72c9faa2015-01-09 10:06:39 -0800444 const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000445
joshualitt2dd1ae02014-12-03 06:24:10 -0800446 const GrAttribute* inPosition() const { return fInPosition; }
447 const GrAttribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
448 const GrAttribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700449
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000450 inline Mode getMode() const { return fMode; }
451
joshualittb0a8a372014-09-23 09:50:21 -0700452 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453 public:
joshualitteb2a6762014-12-04 11:35:33 -0800454 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800455 const GrBatchTracker&)
456 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000457
mtklein72c9faa2015-01-09 10:06:39 -0800458 void emitCode(const EmitArgs& args) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -0800459 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800460 GrGLGPBuilder* pb = args.fPB;
461 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800462 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000463
joshualitt74077b92014-10-24 11:26:03 -0700464 GrGLVertToFrag offsets0(kVec2f_GrSLType);
465 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700466 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800467 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700468
469 GrGLVertToFrag offsets1(kVec2f_GrSLType);
470 args.fPB->addVarying("EllipseOffsets1", &offsets1);
471 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800472 ee.inEllipseOffsets1()->fName);
473
joshualitt9b989322014-12-15 14:16:27 -0800474 // Setup pass through color
475 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
476 &fColorUniform);
477
joshualitt2dd1ae02014-12-03 06:24:10 -0800478 // setup coord outputs
479 vsBuilder->codeAppendf("%s = %s;", vsBuilder->positionCoords(), ee.inPosition()->fName);
480 vsBuilder->codeAppendf("%s = %s;", vsBuilder->localCoords(), ee.inPosition()->fName);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481
joshualittee2af952014-12-30 09:04:15 -0800482 // setup uniform viewMatrix
483 this->addUniformViewMatrix(pb);
484
joshualitt4973d9d2014-11-08 09:24:25 -0800485 // setup position varying
486 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", vsBuilder->glPosition(),
joshualittee2af952014-12-30 09:04:15 -0800487 this->uViewM(), ee.inPosition()->fName);
joshualitt4973d9d2014-11-08 09:24:25 -0800488
joshualittc369e7c2014-10-22 10:56:26 -0700489 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700490 SkAssertResult(fsBuilder->enableFeature(
491 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000492 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700493 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
494 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
495 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
496 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
497 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
498 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
499 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500
joshualitt74077b92014-10-24 11:26:03 -0700501 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000502 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700503 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
504 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800505 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000506 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700507 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
508 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000509 } else {
joshualitt74077b92014-10-24 11:26:03 -0700510 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000511 }
512
513 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800514 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700515 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
516 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
517 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
518 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
519 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
520 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
521 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
522 offsets1.fsIn());
523 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
524 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000525 }
526
joshualitt2dd1ae02014-12-03 06:24:10 -0800527 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000528 }
529
joshualitt87f48d92014-12-04 10:41:40 -0800530 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800531 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800532 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700533 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800534 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700535 const DIEllipseEdgeEffect& ellipseEffect = processor.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800536 uint16_t key = ellipseEffect.getMode();
537 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x1 << 8 :
538 0x0;
539 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000540 }
541
joshualitt9b989322014-12-15 14:16:27 -0800542 virtual void setData(const GrGLProgramDataManager& pdman,
543 const GrPrimitiveProcessor& gp,
544 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800545 this->setUniformViewMatrix(pdman, gp.viewMatrix());
546
joshualitt9b989322014-12-15 14:16:27 -0800547 const BatchTracker& local = bt.cast<BatchTracker>();
548 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
549 GrGLfloat c[4];
550 GrColorToRGBAFloat(local.fColor, c);
551 pdman.set4fv(fColorUniform, 1, c);
552 fColor = local.fColor;
553 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000554 }
555
556 private:
joshualitt9b989322014-12-15 14:16:27 -0800557 GrColor fColor;
558 UniformHandle fColorUniform;
559
joshualitt249af152014-09-15 11:41:13 -0700560 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000561 };
562
joshualitteb2a6762014-12-04 11:35:33 -0800563 virtual void getGLProcessorKey(const GrBatchTracker& bt,
564 const GrGLCaps& caps,
565 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
566 GLProcessor::GenKey(*this, bt, caps, b);
567 }
568
mtklein72c9faa2015-01-09 10:06:39 -0800569 GrGLGeometryProcessor* createGLInstance(const GrBatchTracker& bt) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800570 return SkNEW_ARGS(GLProcessor, (*this, bt));
571 }
572
joshualitt9b989322014-12-15 14:16:27 -0800573 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
574 BatchTracker* local = bt->cast<BatchTracker>();
575 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800576 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800577 }
578
joshualitt290c09b2014-12-19 13:45:20 -0800579 bool onCanMakeEqual(const GrBatchTracker& m,
580 const GrGeometryProcessor& that,
581 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800582 const BatchTracker& mine = m.cast<BatchTracker>();
583 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800584 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
585 that, theirs.fUsesLocalCoords) &&
586 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800587 theirs.fInputColorType, theirs.fColor);
588 }
589
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000590private:
joshualitt8059eb92014-12-29 15:10:07 -0800591 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
592 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800593 this->initClassID<DIEllipseEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800594 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
595 fInEllipseOffsets0 = &this->addVertexAttrib(GrAttribute("inEllipseOffsets0",
596 kVec2f_GrVertexAttribType));
597 fInEllipseOffsets1 = &this->addVertexAttrib(GrAttribute("inEllipseOffsets1",
598 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000599 fMode = mode;
600 }
601
mtklein72c9faa2015-01-09 10:06:39 -0800602 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700603 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000604 return eee.fMode == fMode;
605 }
606
mtklein72c9faa2015-01-09 10:06:39 -0800607 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800608 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700609 }
610
joshualitt9b989322014-12-15 14:16:27 -0800611 struct BatchTracker {
612 GrGPInput fInputColorType;
613 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800614 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800615 };
616
joshualitt2dd1ae02014-12-03 06:24:10 -0800617 const GrAttribute* fInPosition;
618 const GrAttribute* fInEllipseOffsets0;
619 const GrAttribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000620 Mode fMode;
621
joshualittb0a8a372014-09-23 09:50:21 -0700622 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000623
joshualitt249af152014-09-15 11:41:13 -0700624 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000625};
626
joshualittb0a8a372014-09-23 09:50:21 -0700627GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000628
joshualittb0a8a372014-09-23 09:50:21 -0700629GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
630 GrContext* context,
631 const GrDrawTargetCaps&,
632 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800633 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
634 GrProcessorUnitTest::TestMatrix(random),
635 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000636}
637
638///////////////////////////////////////////////////////////////////////////////
639
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000640void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000641 SkSafeSetNull(fRRectIndexBuffer);
joshualitt58a65442014-10-22 20:53:24 -0700642 SkSafeSetNull(fStrokeRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000643}
644
joshualitt9853cce2014-11-17 14:22:48 -0800645bool GrOvalRenderer::drawOval(GrDrawTarget* target,
646 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800647 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800648 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800649 bool useAA,
650 const SkRect& oval,
651 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000652{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000653 bool useCoverageAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -0800654 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -0800655 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000656
657 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000658 return false;
659 }
660
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000661 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800662 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
663 this->drawCircle(target, drawState, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000664 // if we have shader derivative support, render as device-independent
665 } else if (target->caps()->shaderDerivativeSupport()) {
joshualitt8059eb92014-12-29 15:10:07 -0800666 return this->drawDIEllipse(target, drawState, color, viewMatrix, useCoverageAA, oval,
667 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000668 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800669 } else if (viewMatrix.rectStaysRect()) {
670 return this->drawEllipse(target, drawState, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000671 } else {
672 return false;
673 }
674
675 return true;
676}
677
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000678///////////////////////////////////////////////////////////////////////////////
679
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000680void GrOvalRenderer::drawCircle(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800681 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800682 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800683 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000684 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000685 const SkRect& circle,
joshualitt8059eb92014-12-29 15:10:07 -0800686 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000687 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800688 viewMatrix.mapPoints(&center, 1);
689 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
690 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000691
joshualittd27f73e2014-12-29 07:43:36 -0800692 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800693 if (!viewMatrix.invert(&invert)) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000694 return;
695 }
696
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000697 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000698 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
699 SkStrokeRec::kHairline_Style == style;
700 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000701
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000702 SkScalar innerRadius = 0.0f;
703 SkScalar outerRadius = radius;
704 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000705 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000706 if (SkScalarNearlyZero(strokeWidth)) {
707 halfWidth = SK_ScalarHalf;
708 } else {
709 halfWidth = SkScalarHalf(strokeWidth);
710 }
711
712 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000713 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000714 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000715 }
716 }
717
joshualitt56995b52014-12-11 15:44:02 -0800718 SkAutoTUnref<GrGeometryProcessor> gp(
joshualitt8059eb92014-12-29 15:10:07 -0800719 CircleEdgeEffect::Create(color, isStrokeOnly && innerRadius > 0,invert));
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000720
joshualitt2dd1ae02014-12-03 06:24:10 -0800721 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
722 SkASSERT(gp->getVertexStride() == sizeof(CircleVertex));
723 if (!geo.succeeded()) {
724 SkDebugf("Failed to get space for vertices!\n");
725 return;
726 }
727
728 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
729
bsalomonce1c8862014-12-15 07:11:22 -0800730 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
731 // computation because the computed alpha is zero, rather than 50%, at the radius.
732 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
733 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000734 outerRadius += SK_ScalarHalf;
735 innerRadius -= SK_ScalarHalf;
736
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000737 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000738 center.fX - outerRadius,
739 center.fY - outerRadius,
740 center.fX + outerRadius,
741 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000742 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000743
bsalomonce1c8862014-12-15 07:11:22 -0800744 // The inner radius in the vertex data must be specified in normalized space.
745 innerRadius = innerRadius / outerRadius;
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000746 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
bsalomonce1c8862014-12-15 07:11:22 -0800747 verts[0].fOffset = SkPoint::Make(-1, -1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000748 verts[0].fOuterRadius = outerRadius;
749 verts[0].fInnerRadius = innerRadius;
750
joshualitt5ead6da2014-10-22 16:00:29 -0700751 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
bsalomonce1c8862014-12-15 07:11:22 -0800752 verts[1].fOffset = SkPoint::Make(-1, 1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000753 verts[1].fOuterRadius = outerRadius;
754 verts[1].fInnerRadius = innerRadius;
755
joshualitt5ead6da2014-10-22 16:00:29 -0700756 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
bsalomonce1c8862014-12-15 07:11:22 -0800757 verts[2].fOffset = SkPoint::Make(1, 1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000758 verts[2].fOuterRadius = outerRadius;
759 verts[2].fInnerRadius = innerRadius;
760
joshualitt5ead6da2014-10-22 16:00:29 -0700761 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
bsalomonce1c8862014-12-15 07:11:22 -0800762 verts[3].fOffset = SkPoint::Make(1, -1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000763 verts[3].fOuterRadius = outerRadius;
764 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000765
joshualitt5531d512014-12-17 15:50:11 -0800766 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -0800767 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -0700768 target->resetIndexSource();
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000769}
770
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000771///////////////////////////////////////////////////////////////////////////////
772
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000773bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800774 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800775 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800776 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000777 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000778 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -0800779 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000780#ifdef SK_DEBUG
781 {
782 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -0800783 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000784 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000785 }
786#endif
787
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000788 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000789 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800790 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000791 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
792 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -0800793 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
794 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
795 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
796 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000797
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000798 // do (potentially) anisotropic mapping of stroke
799 SkVector scaledStroke;
800 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -0800801 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
802 viewMatrix[SkMatrix::kMSkewY]));
803 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
804 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000805
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000806 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000807 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
808 SkStrokeRec::kHairline_Style == style;
809 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000810
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000811 SkScalar innerXRadius = 0;
812 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000813 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000814 if (SkScalarNearlyZero(scaledStroke.length())) {
815 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
816 } else {
817 scaledStroke.scale(SK_ScalarHalf);
818 }
819
820 // we only handle thick strokes for near-circular ellipses
821 if (scaledStroke.length() > SK_ScalarHalf &&
822 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
823 return false;
824 }
825
826 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
827 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
828 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
829 return false;
830 }
831
832 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000833 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000834 innerXRadius = xRadius - scaledStroke.fX;
835 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000836 }
837
838 xRadius += scaledStroke.fX;
839 yRadius += scaledStroke.fY;
840 }
841
joshualittd27f73e2014-12-29 07:43:36 -0800842 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800843 if (!viewMatrix.invert(&invert)) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000844 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000845 }
846
joshualitt56995b52014-12-11 15:44:02 -0800847 SkAutoTUnref<GrGeometryProcessor> gp(
joshualitt8059eb92014-12-29 15:10:07 -0800848 EllipseEdgeEffect::Create(color,
849 isStrokeOnly && innerXRadius > 0 && innerYRadius > 0,
joshualittd27f73e2014-12-29 07:43:36 -0800850 invert));
joshualitt2dd1ae02014-12-03 06:24:10 -0800851
852 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
853 SkASSERT(gp->getVertexStride() == sizeof(EllipseVertex));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000854 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -0700855 SkDebugf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000856 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000857 }
858
859 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
860
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000861 // Compute the reciprocals of the radii here to save time in the shader
862 SkScalar xRadRecip = SkScalarInvert(xRadius);
863 SkScalar yRadRecip = SkScalarInvert(yRadius);
864 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
865 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000866
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000867 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000868 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000869 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000870 xRadius += SK_ScalarHalf;
871 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000872
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000873 SkRect bounds = SkRect::MakeLTRB(
874 center.fX - xRadius,
875 center.fY - yRadius,
876 center.fX + xRadius,
877 center.fY + yRadius
878 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000879
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000880 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000881 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
882 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
883 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000884
joshualitt5ead6da2014-10-22 16:00:29 -0700885 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
886 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000887 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
888 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000889
joshualitt5ead6da2014-10-22 16:00:29 -0700890 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
891 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000892 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
893 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000894
joshualitt5ead6da2014-10-22 16:00:29 -0700895 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
896 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000897 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
898 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000899
joshualitt5531d512014-12-17 15:50:11 -0800900 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -0800901 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -0700902 target->resetIndexSource();
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000903
904 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000905}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000906
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000907bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800908 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800909 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800910 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000911 bool useCoverageAA,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000912 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -0800913 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000914 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000915 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000916 SkScalar yRadius = SkScalarHalf(ellipse.height());
917
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000918 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000919 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000920 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000921 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000922 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
923
924 SkScalar innerXRadius = 0;
925 SkScalar innerYRadius = 0;
926 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
927 SkScalar strokeWidth = stroke.getWidth();
928
929 if (SkScalarNearlyZero(strokeWidth)) {
930 strokeWidth = SK_ScalarHalf;
931 } else {
932 strokeWidth *= SK_ScalarHalf;
933 }
934
935 // we only handle thick strokes for near-circular ellipses
936 if (strokeWidth > SK_ScalarHalf &&
937 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
938 return false;
939 }
940
941 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
942 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
943 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
944 return false;
945 }
946
947 // set inner radius (if needed)
948 if (SkStrokeRec::kStroke_Style == style) {
949 innerXRadius = xRadius - strokeWidth;
950 innerYRadius = yRadius - strokeWidth;
951 }
952
953 xRadius += strokeWidth;
954 yRadius += strokeWidth;
955 }
956 if (DIEllipseEdgeEffect::kStroke == mode) {
957 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
958 DIEllipseEdgeEffect::kFill;
959 }
960 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
961 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
962
joshualitt8059eb92014-12-29 15:10:07 -0800963 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(color, viewMatrix, mode));
joshualitt2dd1ae02014-12-03 06:24:10 -0800964
965 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
966 SkASSERT(gp->getVertexStride() == sizeof(DIEllipseVertex));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000967 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -0700968 SkDebugf("Failed to get space for vertices!\n");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000969 return false;
970 }
971
972 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
973
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000974 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -0800975 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
976 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
977 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
978 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000979 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
980 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
981 // This adjusts the "radius" to include the half-pixel border
982 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
983 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
984
985 SkRect bounds = SkRect::MakeLTRB(
986 center.fX - xRadius - geoDx,
987 center.fY - yRadius - geoDy,
988 center.fX + xRadius + geoDx,
989 center.fY + yRadius + geoDy
990 );
991
992 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
993 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
994 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
995
joshualitt5ead6da2014-10-22 16:00:29 -0700996 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
997 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
998 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000999
joshualitt5ead6da2014-10-22 16:00:29 -07001000 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1001 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1002 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001003
joshualitt5ead6da2014-10-22 16:00:29 -07001004 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1005 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1006 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001007
joshualitt5531d512014-12-17 15:50:11 -08001008 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -08001009 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -07001010 target->resetIndexSource();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001011
1012 return true;
1013}
1014
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001015///////////////////////////////////////////////////////////////////////////////
1016
1017static const uint16_t gRRectIndices[] = {
1018 // corners
1019 0, 1, 5, 0, 5, 4,
1020 2, 3, 7, 2, 7, 6,
1021 8, 9, 13, 8, 13, 12,
1022 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001023
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001024 // edges
1025 1, 2, 6, 1, 6, 5,
1026 4, 5, 9, 4, 9, 8,
1027 6, 7, 11, 6, 11, 10,
1028 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001029
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001030 // center
1031 // we place this at the end so that we can ignore these indices when rendering stroke-only
1032 5, 6, 10, 5, 10, 9
1033};
1034
joshualitt5ead6da2014-10-22 16:00:29 -07001035static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1036static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1037static const int kVertsPerRRect = 16;
1038static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001039
joshualitt5531d512014-12-17 15:50:11 -08001040GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
joshualitt5ead6da2014-10-22 16:00:29 -07001041 if (isStrokeOnly) {
1042 if (NULL == fStrokeRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001043 fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1044 kIndicesPerStrokeRRect,
1045 kNumRRectsInIndexBuffer,
1046 kVertsPerRRect);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001047 }
joshualitt5ead6da2014-10-22 16:00:29 -07001048 return fStrokeRRectIndexBuffer;
1049 } else {
1050 if (NULL == fRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001051 fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1052 kIndicesPerRRect,
1053 kNumRRectsInIndexBuffer,
1054 kVertsPerRRect);
joshualitt5ead6da2014-10-22 16:00:29 -07001055 }
1056 return fRRectIndexBuffer;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001057 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001058}
1059
joshualitt9853cce2014-11-17 14:22:48 -08001060bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
1061 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -08001062 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001063 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001064 bool useAA,
1065 const SkRRect& origOuter,
1066 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001067 bool applyAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -08001068 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -08001069 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001070 GrDrawState::AutoRestoreEffects are;
1071 if (!origInner.isEmpty()) {
1072 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001073 if (!viewMatrix.isIdentity()) {
1074 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001075 return false;
1076 }
1077 }
joshualittb0a8a372014-09-23 09:50:21 -07001078 GrPrimitiveEdgeType edgeType = applyAA ?
1079 kInverseFillAA_GrProcessorEdgeType :
1080 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001081 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001082 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1083 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001084 return false;
1085 }
joshualitt9853cce2014-11-17 14:22:48 -08001086 are.set(drawState);
1087 drawState->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001088 }
1089
1090 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
joshualitt8059eb92014-12-29 15:10:07 -08001091 if (this->drawRRect(target, drawState, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001092 return true;
1093 }
1094
1095 SkASSERT(!origOuter.isEmpty());
1096 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001097 if (!viewMatrix.isIdentity()) {
1098 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001099 return false;
1100 }
1101 }
joshualittb0a8a372014-09-23 09:50:21 -07001102 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
1103 kFillBW_GrProcessorEdgeType;
1104 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001105 if (NULL == effect) {
1106 return false;
1107 }
1108 if (!are.isSet()) {
joshualitt9853cce2014-11-17 14:22:48 -08001109 are.set(drawState);
bsalomon8af05232014-06-03 06:34:58 -07001110 }
joshualittd27f73e2014-12-29 07:43:36 -08001111
1112 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001113 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001114 return false;
1115 }
joshualittd27f73e2014-12-29 07:43:36 -08001116
joshualitt9853cce2014-11-17 14:22:48 -08001117 drawState->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001118 SkRect bounds = outer->getBounds();
1119 if (applyAA) {
1120 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1121 }
joshualitt8059eb92014-12-29 15:10:07 -08001122 target->drawRect(drawState, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001123 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001124}
1125
joshualitt9853cce2014-11-17 14:22:48 -08001126bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
1127 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -08001128 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001129 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001130 bool useAA,
1131 const SkRRect& rrect,
1132 const SkStrokeRec& stroke) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001133 if (rrect.isOval()) {
joshualitt8059eb92014-12-29 15:10:07 -08001134 return this->drawOval(target, drawState, color, viewMatrix, useAA, rrect.getBounds(),
1135 stroke);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001136 }
1137
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001138 bool useCoverageAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -08001139 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -08001140 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001141
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00001142 // only anti-aliased rrects for now
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001143 if (!useCoverageAA) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00001144 return false;
1145 }
1146
joshualitt8059eb92014-12-29 15:10:07 -08001147 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001148 return false;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001149 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001150
1151 // do any matrix crunching before we reset the draw state for device coords
1152 const SkRect& rrectBounds = rrect.getBounds();
1153 SkRect bounds;
joshualitt8059eb92014-12-29 15:10:07 -08001154 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001155
1156 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001157 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1158 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1159 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1160 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001161
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001162 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001163
1164 // do (potentially) anisotropic mapping of stroke
1165 SkVector scaledStroke;
1166 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001167
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001168 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1169 SkStrokeRec::kHairline_Style == style;
1170 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1171
1172 if (hasStroke) {
1173 if (SkStrokeRec::kHairline_Style == style) {
1174 scaledStroke.set(1, 1);
1175 } else {
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.org0a09d712014-04-09 21:26:11 +00001180 }
1181
1182 // if half of strokewidth is greater than radius, we don't handle that right now
1183 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1184 return false;
1185 }
1186 }
1187
1188 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1189 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1190 // patch will have fractional coverage. This only matters when the interior is actually filled.
1191 // We could consider falling back to rect rendering here, since a tiny radius is
1192 // indistinguishable from a square corner.
1193 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001194 return false;
1195 }
1196
1197 // reset to device coordinates
joshualittd27f73e2014-12-29 07:43:36 -08001198 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001199 if (!viewMatrix.invert(&invert)) {
joshualittd27f73e2014-12-29 07:43:36 -08001200 SkDebugf("Failed to invert\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001201 return false;
1202 }
1203
joshualitt5531d512014-12-17 15:50:11 -08001204 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001205 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -07001206 SkDebugf("Failed to create index buffer!\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001207 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001208 }
1209
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001210 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001211 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001212 SkScalar innerRadius = 0.0f;
1213 SkScalar outerRadius = xRadius;
1214 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001215 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001216 if (SkScalarNearlyZero(scaledStroke.fX)) {
1217 halfWidth = SK_ScalarHalf;
1218 } else {
1219 halfWidth = SkScalarHalf(scaledStroke.fX);
1220 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001221
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001222 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001223 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001224 }
1225 outerRadius += halfWidth;
1226 bounds.outset(halfWidth, halfWidth);
1227 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001228
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001229 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001230
joshualitt8059eb92014-12-29 15:10:07 -08001231 SkAutoTUnref<GrGeometryProcessor> effect(CircleEdgeEffect::Create(color,
1232 isStrokeOnly,
joshualittd27f73e2014-12-29 07:43:36 -08001233 invert));
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001234
joshualitt2dd1ae02014-12-03 06:24:10 -08001235 GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
1236 SkASSERT(effect->getVertexStride() == sizeof(CircleVertex));
1237 if (!geo.succeeded()) {
1238 SkDebugf("Failed to get space for vertices!\n");
1239 return false;
1240 }
1241 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
1242
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001243 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001244 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1245 // Second, the outer radius is used to compute the verts of the bounding box that is
1246 // rendered and the outset ensures the box will cover all partially covered by the rrect
1247 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001248 outerRadius += SK_ScalarHalf;
1249 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001250
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001251 // Expand the rect so all the pixels will be captured.
1252 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001253
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001254 SkScalar yCoords[4] = {
1255 bounds.fTop,
1256 bounds.fTop + outerRadius,
1257 bounds.fBottom - outerRadius,
1258 bounds.fBottom
1259 };
bsalomonce1c8862014-12-15 07:11:22 -08001260 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1261 // The inner radius in the vertex data must be specified in normalized space.
1262 innerRadius = innerRadius / outerRadius;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001263 for (int i = 0; i < 4; ++i) {
1264 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
bsalomonce1c8862014-12-15 07:11:22 -08001265 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001266 verts->fOuterRadius = outerRadius;
1267 verts->fInnerRadius = innerRadius;
1268 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001269
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001270 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1271 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1272 verts->fOuterRadius = outerRadius;
1273 verts->fInnerRadius = innerRadius;
1274 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001275
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001276 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1277 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1278 verts->fOuterRadius = outerRadius;
1279 verts->fInnerRadius = innerRadius;
1280 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001281
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001282 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
bsalomonce1c8862014-12-15 07:11:22 -08001283 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001284 verts->fOuterRadius = outerRadius;
1285 verts->fInnerRadius = innerRadius;
1286 verts++;
1287 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001288
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001289 // drop out the middle quad if we're stroked
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001290 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1291 SK_ARRAY_COUNT(gRRectIndices);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001292 target->setIndexSourceToBuffer(indexBuffer);
joshualitt56995b52014-12-11 15:44:02 -08001293 target->drawIndexedInstances(drawState, effect, kTriangles_GrPrimitiveType, 1, 16, indexCnt,
joshualitt9853cce2014-11-17 14:22:48 -08001294 &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001295
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001296 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001297 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001298 SkScalar innerXRadius = 0.0f;
1299 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001300 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001301 if (SkScalarNearlyZero(scaledStroke.length())) {
1302 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1303 } else {
1304 scaledStroke.scale(SK_ScalarHalf);
1305 }
1306
1307 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001308 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001309 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1310 return false;
1311 }
1312
1313 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1314 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1315 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1316 return false;
1317 }
1318
1319 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001320 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001321 innerXRadius = xRadius - scaledStroke.fX;
1322 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001323 }
1324
1325 xRadius += scaledStroke.fX;
1326 yRadius += scaledStroke.fY;
1327 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1328 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001329
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001330 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001331
joshualitt8059eb92014-12-29 15:10:07 -08001332 SkAutoTUnref<GrGeometryProcessor> effect(EllipseEdgeEffect::Create(color,
1333 isStrokeOnly,
joshualittd27f73e2014-12-29 07:43:36 -08001334 invert));
joshualitt2dd1ae02014-12-03 06:24:10 -08001335
1336 GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
1337 SkASSERT(effect->getVertexStride() == sizeof(EllipseVertex));
jvanverth@google.come3647412013-05-08 15:31:43 +00001338 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -07001339 SkDebugf("Failed to get space for vertices!\n");
jvanverth@google.come3647412013-05-08 15:31:43 +00001340 return false;
1341 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001342
joshualitt2dd1ae02014-12-03 06:24:10 -08001343 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001344
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001345 // Compute the reciprocals of the radii here to save time in the shader
1346 SkScalar xRadRecip = SkScalarInvert(xRadius);
1347 SkScalar yRadRecip = SkScalarInvert(yRadius);
1348 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1349 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001350
1351 // Extend the radii out half a pixel to antialias.
1352 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1353 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001354
1355 // Expand the rect so all the pixels will be captured.
1356 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1357
1358 SkScalar yCoords[4] = {
1359 bounds.fTop,
1360 bounds.fTop + yOuterRadius,
1361 bounds.fBottom - yOuterRadius,
1362 bounds.fBottom
1363 };
1364 SkScalar yOuterOffsets[4] = {
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001365 yOuterRadius,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001366 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1367 SK_ScalarNearlyZero,
1368 yOuterRadius
1369 };
1370
1371 for (int i = 0; i < 4; ++i) {
1372 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001373 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001374 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1375 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001376 verts++;
1377
1378 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1379 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001380 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1381 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001382 verts++;
1383
1384 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1385 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001386 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1387 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001388 verts++;
1389
1390 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1391 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001392 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1393 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001394 verts++;
1395 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001396
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001397 // drop out the middle quad if we're stroked
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001398 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1399 SK_ARRAY_COUNT(gRRectIndices);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001400 target->setIndexSourceToBuffer(indexBuffer);
joshualitt56995b52014-12-11 15:44:02 -08001401 target->drawIndexedInstances(drawState, effect, kTriangles_GrPrimitiveType, 1, 16, indexCnt,
joshualitt9853cce2014-11-17 14:22:48 -08001402 &bounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001403 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001404
joshualitt5ead6da2014-10-22 16:00:29 -07001405 target->resetIndexSource();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001406 return true;
1407}