blob: b5f3f5a8feed0cbedc52f22f26840114f37bedcc [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
joshualittabb52a12015-01-13 15:02:10 -080086 void onEmitCode(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
joshualittabb52a12015-01-13 15:02:10 -080092 // emit attributes
93 vsBuilder->emitAttributes(ce);
94
joshualitt74077b92014-10-24 11:26:03 -070095 GrGLVertToFrag v(kVec4f_GrSLType);
96 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -080097 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000098
joshualitt9b989322014-12-15 14:16:27 -080099 // Setup pass through color
100 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
101 &fColorUniform);
102
joshualittee2af952014-12-30 09:04:15 -0800103 // setup uniform viewMatrix
104 this->addUniformViewMatrix(pb);
105
joshualittabb52a12015-01-13 15:02:10 -0800106 // Setup position
107 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
108 ce.inPosition()->fName);
109
110 // emit transforms
111 this->emitTransforms(args.fPB, this->position(), ce.inPosition()->fName,
112 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);;
joshualitt4973d9d2014-11-08 09:24:25 -0800113
joshualittc369e7c2014-10-22 10:56:26 -0700114 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700115 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800116 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800117 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800118 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
119 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700120 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000121 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000122
joshualitt2dd1ae02014-12-03 06:24:10 -0800123 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124 }
125
joshualitt87f48d92014-12-04 10:41:40 -0800126 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800127 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800128 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700129 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800130 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700131 const CircleEdgeEffect& circleEffect = processor.cast<CircleEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800132 uint16_t key = circleEffect.isStroked() ? 0x1 : 0x0;
133 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
134 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000135 }
136
joshualitt9b989322014-12-15 14:16:27 -0800137 virtual void setData(const GrGLProgramDataManager& pdman,
138 const GrPrimitiveProcessor& gp,
139 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800140 this->setUniformViewMatrix(pdman, gp.viewMatrix());
141
joshualitt9b989322014-12-15 14:16:27 -0800142 const BatchTracker& local = bt.cast<BatchTracker>();
143 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
144 GrGLfloat c[4];
145 GrColorToRGBAFloat(local.fColor, c);
146 pdman.set4fv(fColorUniform, 1, c);
147 fColor = local.fColor;
148 }
149 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000150
151 private:
joshualitt9b989322014-12-15 14:16:27 -0800152 GrColor fColor;
153 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700154 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000155 };
156
joshualitteb2a6762014-12-04 11:35:33 -0800157 virtual void getGLProcessorKey(const GrBatchTracker& bt,
158 const GrGLCaps& caps,
159 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
160 GLProcessor::GenKey(*this, bt, caps, b);
161 }
162
joshualittabb52a12015-01-13 15:02:10 -0800163 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
164 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800165 return SkNEW_ARGS(GLProcessor, (*this, bt));
166 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000167
joshualitt9b989322014-12-15 14:16:27 -0800168 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
169 BatchTracker* local = bt->cast<BatchTracker>();
170 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800171 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800172 }
173
joshualitt290c09b2014-12-19 13:45:20 -0800174 bool onCanMakeEqual(const GrBatchTracker& m,
175 const GrGeometryProcessor& that,
176 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800177 const BatchTracker& mine = m.cast<BatchTracker>();
178 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800179 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
180 that, theirs.fUsesLocalCoords) &&
181 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800182 theirs.fInputColorType, theirs.fColor);
183 }
184
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000185private:
joshualittd27f73e2014-12-29 07:43:36 -0800186 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800187 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800188 this->initClassID<CircleEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800189 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
190 fInCircleEdge = &this->addVertexAttrib(GrAttribute("inCircleEdge",
191 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000192 fStroke = stroke;
193 }
194
mtklein72c9faa2015-01-09 10:06:39 -0800195 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700196 const CircleEdgeEffect& cee = other.cast<CircleEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197 return cee.fStroke == fStroke;
198 }
199
mtklein72c9faa2015-01-09 10:06:39 -0800200 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800201 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700202 }
203
joshualitt9b989322014-12-15 14:16:27 -0800204 struct BatchTracker {
205 GrGPInput fInputColorType;
206 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800207 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800208 };
209
joshualitt2dd1ae02014-12-03 06:24:10 -0800210 const GrAttribute* fInPosition;
211 const GrAttribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000212 bool fStroke;
213
joshualittb0a8a372014-09-23 09:50:21 -0700214 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215
joshualitt249af152014-09-15 11:41:13 -0700216 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217};
218
joshualittb0a8a372014-09-23 09:50:21 -0700219GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220
joshualittb0a8a372014-09-23 09:50:21 -0700221GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random,
222 GrContext* context,
223 const GrDrawTargetCaps&,
224 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800225 return CircleEdgeEffect::Create(GrRandomColor(random),
226 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800227 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228}
229
230///////////////////////////////////////////////////////////////////////////////
231
232/**
233 * 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 +0000234 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
235 * in both x and y directions.
236 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000237 * 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 +0000238 */
239
joshualitt249af152014-09-15 11:41:13 -0700240class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241public:
joshualittd27f73e2014-12-29 07:43:36 -0800242 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) {
243 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000244 }
245
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246 virtual ~EllipseEdgeEffect() {}
247
mtklein72c9faa2015-01-09 10:06:39 -0800248 const char* name() const SK_OVERRIDE { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800249
250 const GrAttribute* inPosition() const { return fInPosition; }
251 const GrAttribute* inEllipseOffset() const { return fInEllipseOffset; }
252 const GrAttribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt249af152014-09-15 11:41:13 -0700253
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254 inline bool isStroked() const { return fStroke; }
255
joshualittb0a8a372014-09-23 09:50:21 -0700256 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000257 public:
joshualitteb2a6762014-12-04 11:35:33 -0800258 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800259 const GrBatchTracker&)
260 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261
joshualittabb52a12015-01-13 15:02:10 -0800262 void onEmitCode(EmitArgs& args) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -0800263 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800264 GrGLGPBuilder* pb = args.fPB;
265 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800266 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000267
joshualittabb52a12015-01-13 15:02:10 -0800268 // emit attributes
269 vsBuilder->emitAttributes(ee);
270
joshualitt74077b92014-10-24 11:26:03 -0700271 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
272 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700273 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800274 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000275
joshualitt74077b92014-10-24 11:26:03 -0700276 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
277 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
278 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800279 ee.inEllipseRadii()->fName);
280
joshualitt9b989322014-12-15 14:16:27 -0800281 // Setup pass through color
282 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
283 &fColorUniform);
284
joshualittee2af952014-12-30 09:04:15 -0800285 // setup uniform viewMatrix
286 this->addUniformViewMatrix(pb);
287
joshualittabb52a12015-01-13 15:02:10 -0800288 // Setup position
289 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
290 ee.inPosition()->fName);
291
292 // emit transforms
293 this->emitTransforms(args.fPB, this->position(), ee.inPosition()->fName,
294 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
joshualittc369e7c2014-10-22 10:56:26 -0700297 GrGLGPFragmentBuilder* 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
joshualitt87f48d92014-12-04 10:41:40 -0800323 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800324 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800325 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700326 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800327 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700328 const EllipseEdgeEffect& ellipseEffect = processor.cast<EllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800329 uint16_t key = ellipseEffect.isStroked() ? 0x1 : 0x0;
330 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x2 : 0x0;
331 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000332 }
333
joshualitt9b989322014-12-15 14:16:27 -0800334 virtual void setData(const GrGLProgramDataManager& pdman,
335 const GrPrimitiveProcessor& gp,
336 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800337 this->setUniformViewMatrix(pdman, gp.viewMatrix());
338
joshualitt9b989322014-12-15 14:16:27 -0800339 const BatchTracker& local = bt.cast<BatchTracker>();
340 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
341 GrGLfloat c[4];
342 GrColorToRGBAFloat(local.fColor, c);
343 pdman.set4fv(fColorUniform, 1, c);
344 fColor = local.fColor;
345 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 }
347
348 private:
joshualitt9b989322014-12-15 14:16:27 -0800349 GrColor fColor;
350 UniformHandle fColorUniform;
351
joshualitt249af152014-09-15 11:41:13 -0700352 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353 };
354
joshualitteb2a6762014-12-04 11:35:33 -0800355 virtual void getGLProcessorKey(const GrBatchTracker& bt,
356 const GrGLCaps& caps,
357 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
358 GLProcessor::GenKey(*this, bt, caps, b);
359 }
360
joshualittabb52a12015-01-13 15:02:10 -0800361 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
362 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800363 return SkNEW_ARGS(GLProcessor, (*this, bt));
364 }
365
joshualitt9b989322014-12-15 14:16:27 -0800366 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
367 BatchTracker* local = bt->cast<BatchTracker>();
368 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800369 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800370 }
371
joshualitt290c09b2014-12-19 13:45:20 -0800372 bool onCanMakeEqual(const GrBatchTracker& m,
373 const GrGeometryProcessor& that,
374 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800375 const BatchTracker& mine = m.cast<BatchTracker>();
376 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800377 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
378 that, theirs.fUsesLocalCoords) &&
379 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800380 theirs.fInputColorType, theirs.fColor);
381 }
382
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000383private:
joshualittd27f73e2014-12-29 07:43:36 -0800384 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix)
joshualitt8059eb92014-12-29 15:10:07 -0800385 : INHERITED(color, SkMatrix::I(), localMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800386 this->initClassID<EllipseEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800387 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
388 fInEllipseOffset = &this->addVertexAttrib(GrAttribute("inEllipseOffset",
389 kVec2f_GrVertexAttribType));
390 fInEllipseRadii = &this->addVertexAttrib(GrAttribute("inEllipseRadii",
391 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392 fStroke = stroke;
393 }
394
mtklein72c9faa2015-01-09 10:06:39 -0800395 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700396 const EllipseEdgeEffect& eee = other.cast<EllipseEdgeEffect>();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000397 return eee.fStroke == fStroke;
398 }
399
mtklein72c9faa2015-01-09 10:06:39 -0800400 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800401 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700402 }
403
joshualitt9b989322014-12-15 14:16:27 -0800404 struct BatchTracker {
405 GrGPInput fInputColorType;
406 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800407 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800408 };
409
joshualitt2dd1ae02014-12-03 06:24:10 -0800410 const GrAttribute* fInPosition;
411 const GrAttribute* fInEllipseOffset;
412 const GrAttribute* fInEllipseRadii;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000413 bool fStroke;
414
joshualittb0a8a372014-09-23 09:50:21 -0700415 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000416
joshualitt249af152014-09-15 11:41:13 -0700417 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000418};
419
joshualittb0a8a372014-09-23 09:50:21 -0700420GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000421
joshualittb0a8a372014-09-23 09:50:21 -0700422GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random,
423 GrContext* context,
424 const GrDrawTargetCaps&,
425 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800426 return EllipseEdgeEffect::Create(GrRandomColor(random),
427 random->nextBool(),
joshualittd27f73e2014-12-29 07:43:36 -0800428 GrProcessorUnitTest::TestMatrix(random));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000429}
430
431///////////////////////////////////////////////////////////////////////////////
432
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000434 * 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 +0000435 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
436 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
437 * using differentials.
438 *
439 * The result is device-independent and can be used with any affine matrix.
440 */
441
joshualitt249af152014-09-15 11:41:13 -0700442class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000443public:
444 enum Mode { kStroke = 0, kHairline, kFill };
445
joshualitt8059eb92014-12-29 15:10:07 -0800446 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) {
447 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000448 }
449
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000450 virtual ~DIEllipseEdgeEffect() {}
451
mtklein72c9faa2015-01-09 10:06:39 -0800452 const char* name() const SK_OVERRIDE { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453
joshualitt2dd1ae02014-12-03 06:24:10 -0800454 const GrAttribute* inPosition() const { return fInPosition; }
455 const GrAttribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
456 const GrAttribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt249af152014-09-15 11:41:13 -0700457
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000458 inline Mode getMode() const { return fMode; }
459
joshualittb0a8a372014-09-23 09:50:21 -0700460 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000461 public:
joshualitteb2a6762014-12-04 11:35:33 -0800462 GLProcessor(const GrGeometryProcessor&,
joshualitt9b989322014-12-15 14:16:27 -0800463 const GrBatchTracker&)
464 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465
joshualittabb52a12015-01-13 15:02:10 -0800466 void onEmitCode(EmitArgs& args) SK_OVERRIDE {
joshualitt2dd1ae02014-12-03 06:24:10 -0800467 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800468 GrGLGPBuilder* pb = args.fPB;
469 const BatchTracker& local = args.fBT.cast<BatchTracker>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800470 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000471
joshualittabb52a12015-01-13 15:02:10 -0800472 // emit attributes
473 vsBuilder->emitAttributes(ee);
474
joshualitt74077b92014-10-24 11:26:03 -0700475 GrGLVertToFrag offsets0(kVec2f_GrSLType);
476 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700477 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800478 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700479
480 GrGLVertToFrag offsets1(kVec2f_GrSLType);
481 args.fPB->addVarying("EllipseOffsets1", &offsets1);
482 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800483 ee.inEllipseOffsets1()->fName);
484
joshualitt9b989322014-12-15 14:16:27 -0800485 // Setup pass through color
486 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL,
487 &fColorUniform);
488
joshualittee2af952014-12-30 09:04:15 -0800489 // setup uniform viewMatrix
490 this->addUniformViewMatrix(pb);
491
joshualittabb52a12015-01-13 15:02:10 -0800492 // Setup position
493 vsBuilder->codeAppendf("%s = %s * vec3(%s, 1);", this->position(), this->uViewM(),
494 ee.inPosition()->fName);
495
496 // emit transforms
497 this->emitTransforms(args.fPB, this->position(), ee.inPosition()->fName,
498 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800499
joshualittc369e7c2014-10-22 10:56:26 -0700500 GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700501 SkAssertResult(fsBuilder->enableFeature(
502 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000503 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700504 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
505 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
506 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
507 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
508 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
509 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
510 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000511
joshualitt74077b92014-10-24 11:26:03 -0700512 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000513 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700514 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
515 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800516 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700518 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
519 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000520 } else {
joshualitt74077b92014-10-24 11:26:03 -0700521 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000522 }
523
524 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800525 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700526 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
527 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
528 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
529 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
530 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
531 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
532 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
533 offsets1.fsIn());
534 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
535 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000536 }
537
joshualitt2dd1ae02014-12-03 06:24:10 -0800538 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000539 }
540
joshualitt87f48d92014-12-04 10:41:40 -0800541 static void GenKey(const GrGeometryProcessor& processor,
joshualitt9b989322014-12-15 14:16:27 -0800542 const GrBatchTracker& bt,
joshualitt87f48d92014-12-04 10:41:40 -0800543 const GrGLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700544 GrProcessorKeyBuilder* b) {
joshualitt9b989322014-12-15 14:16:27 -0800545 const BatchTracker& local = bt.cast<BatchTracker>();
joshualittb0a8a372014-09-23 09:50:21 -0700546 const DIEllipseEdgeEffect& ellipseEffect = processor.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800547 uint16_t key = ellipseEffect.getMode();
548 key |= local.fUsesLocalCoords && processor.localMatrix().hasPerspective() ? 0x1 << 8 :
549 0x0;
550 b->add32(key << 16 | local.fInputColorType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000551 }
552
joshualitt9b989322014-12-15 14:16:27 -0800553 virtual void setData(const GrGLProgramDataManager& pdman,
554 const GrPrimitiveProcessor& gp,
555 const GrBatchTracker& bt) SK_OVERRIDE {
joshualittee2af952014-12-30 09:04:15 -0800556 this->setUniformViewMatrix(pdman, gp.viewMatrix());
557
joshualitt9b989322014-12-15 14:16:27 -0800558 const BatchTracker& local = bt.cast<BatchTracker>();
559 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) {
560 GrGLfloat c[4];
561 GrColorToRGBAFloat(local.fColor, c);
562 pdman.set4fv(fColorUniform, 1, c);
563 fColor = local.fColor;
564 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000565 }
566
567 private:
joshualitt9b989322014-12-15 14:16:27 -0800568 GrColor fColor;
569 UniformHandle fColorUniform;
570
joshualitt249af152014-09-15 11:41:13 -0700571 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000572 };
573
joshualitteb2a6762014-12-04 11:35:33 -0800574 virtual void getGLProcessorKey(const GrBatchTracker& bt,
575 const GrGLCaps& caps,
576 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
577 GLProcessor::GenKey(*this, bt, caps, b);
578 }
579
joshualittabb52a12015-01-13 15:02:10 -0800580 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
581 const GrGLCaps&) const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800582 return SkNEW_ARGS(GLProcessor, (*this, bt));
583 }
584
joshualitt9b989322014-12-15 14:16:27 -0800585 void initBatchTracker(GrBatchTracker* bt, const InitBT& init) const SK_OVERRIDE {
586 BatchTracker* local = bt->cast<BatchTracker>();
587 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false);
joshualitt290c09b2014-12-19 13:45:20 -0800588 local->fUsesLocalCoords = init.fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800589 }
590
joshualitt290c09b2014-12-19 13:45:20 -0800591 bool onCanMakeEqual(const GrBatchTracker& m,
592 const GrGeometryProcessor& that,
593 const GrBatchTracker& t) const SK_OVERRIDE {
joshualitt9b989322014-12-15 14:16:27 -0800594 const BatchTracker& mine = m.cast<BatchTracker>();
595 const BatchTracker& theirs = t.cast<BatchTracker>();
joshualitt290c09b2014-12-19 13:45:20 -0800596 return CanCombineLocalMatrices(*this, mine.fUsesLocalCoords,
597 that, theirs.fUsesLocalCoords) &&
598 CanCombineOutput(mine.fInputColorType, mine.fColor,
joshualitt9b989322014-12-15 14:16:27 -0800599 theirs.fInputColorType, theirs.fColor);
600 }
601
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000602private:
joshualitt8059eb92014-12-29 15:10:07 -0800603 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode)
604 : INHERITED(color, viewMatrix) {
joshualitteb2a6762014-12-04 11:35:33 -0800605 this->initClassID<DIEllipseEdgeEffect>();
joshualitt2dd1ae02014-12-03 06:24:10 -0800606 fInPosition = &this->addVertexAttrib(GrAttribute("inPosition", kVec2f_GrVertexAttribType));
607 fInEllipseOffsets0 = &this->addVertexAttrib(GrAttribute("inEllipseOffsets0",
608 kVec2f_GrVertexAttribType));
609 fInEllipseOffsets1 = &this->addVertexAttrib(GrAttribute("inEllipseOffsets1",
610 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000611 fMode = mode;
612 }
613
mtklein72c9faa2015-01-09 10:06:39 -0800614 bool onIsEqual(const GrGeometryProcessor& other) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700615 const DIEllipseEdgeEffect& eee = other.cast<DIEllipseEdgeEffect>();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000616 return eee.fMode == fMode;
617 }
618
mtklein72c9faa2015-01-09 10:06:39 -0800619 void onGetInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRIDE {
joshualitt56995b52014-12-11 15:44:02 -0800620 out->setUnknownSingleComponent();
egdaniel1a8ecdf2014-10-03 06:24:12 -0700621 }
622
joshualitt9b989322014-12-15 14:16:27 -0800623 struct BatchTracker {
624 GrGPInput fInputColorType;
625 GrColor fColor;
joshualitt290c09b2014-12-19 13:45:20 -0800626 bool fUsesLocalCoords;
joshualitt9b989322014-12-15 14:16:27 -0800627 };
628
joshualitt2dd1ae02014-12-03 06:24:10 -0800629 const GrAttribute* fInPosition;
630 const GrAttribute* fInEllipseOffsets0;
631 const GrAttribute* fInEllipseOffsets1;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000632 Mode fMode;
633
joshualittb0a8a372014-09-23 09:50:21 -0700634 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000635
joshualitt249af152014-09-15 11:41:13 -0700636 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000637};
638
joshualittb0a8a372014-09-23 09:50:21 -0700639GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000640
joshualittb0a8a372014-09-23 09:50:21 -0700641GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
642 GrContext* context,
643 const GrDrawTargetCaps&,
644 GrTexture* textures[]) {
joshualitt8059eb92014-12-29 15:10:07 -0800645 return DIEllipseEdgeEffect::Create(GrRandomColor(random),
646 GrProcessorUnitTest::TestMatrix(random),
647 (Mode)(random->nextRangeU(0,2)));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000648}
649
650///////////////////////////////////////////////////////////////////////////////
651
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000652void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000653 SkSafeSetNull(fRRectIndexBuffer);
joshualitt58a65442014-10-22 20:53:24 -0700654 SkSafeSetNull(fStrokeRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000655}
656
joshualitt9853cce2014-11-17 14:22:48 -0800657bool GrOvalRenderer::drawOval(GrDrawTarget* target,
658 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800659 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800660 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800661 bool useAA,
662 const SkRect& oval,
663 const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000664{
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000665 bool useCoverageAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -0800666 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -0800667 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000668
669 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000670 return false;
671 }
672
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000673 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800674 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
675 this->drawCircle(target, drawState, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000676 // if we have shader derivative support, render as device-independent
677 } else if (target->caps()->shaderDerivativeSupport()) {
joshualitt8059eb92014-12-29 15:10:07 -0800678 return this->drawDIEllipse(target, drawState, color, viewMatrix, useCoverageAA, oval,
679 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000680 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800681 } else if (viewMatrix.rectStaysRect()) {
682 return this->drawEllipse(target, drawState, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000683 } else {
684 return false;
685 }
686
687 return true;
688}
689
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000690///////////////////////////////////////////////////////////////////////////////
691
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000692void GrOvalRenderer::drawCircle(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800693 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800694 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800695 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000696 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000697 const SkRect& circle,
joshualitt8059eb92014-12-29 15:10:07 -0800698 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000699 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800700 viewMatrix.mapPoints(&center, 1);
701 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
702 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000703
joshualittd27f73e2014-12-29 07:43:36 -0800704 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800705 if (!viewMatrix.invert(&invert)) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000706 return;
707 }
708
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000709 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000710 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
711 SkStrokeRec::kHairline_Style == style;
712 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000713
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000714 SkScalar innerRadius = 0.0f;
715 SkScalar outerRadius = radius;
716 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000717 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000718 if (SkScalarNearlyZero(strokeWidth)) {
719 halfWidth = SK_ScalarHalf;
720 } else {
721 halfWidth = SkScalarHalf(strokeWidth);
722 }
723
724 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000725 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000726 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000727 }
728 }
729
joshualitt56995b52014-12-11 15:44:02 -0800730 SkAutoTUnref<GrGeometryProcessor> gp(
joshualitt8059eb92014-12-29 15:10:07 -0800731 CircleEdgeEffect::Create(color, isStrokeOnly && innerRadius > 0,invert));
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000732
joshualitt2dd1ae02014-12-03 06:24:10 -0800733 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
734 SkASSERT(gp->getVertexStride() == sizeof(CircleVertex));
735 if (!geo.succeeded()) {
736 SkDebugf("Failed to get space for vertices!\n");
737 return;
738 }
739
740 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
741
bsalomonce1c8862014-12-15 07:11:22 -0800742 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
743 // computation because the computed alpha is zero, rather than 50%, at the radius.
744 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
745 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000746 outerRadius += SK_ScalarHalf;
747 innerRadius -= SK_ScalarHalf;
748
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000749 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000750 center.fX - outerRadius,
751 center.fY - outerRadius,
752 center.fX + outerRadius,
753 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000754 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000755
bsalomonce1c8862014-12-15 07:11:22 -0800756 // The inner radius in the vertex data must be specified in normalized space.
757 innerRadius = innerRadius / outerRadius;
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000758 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
bsalomonce1c8862014-12-15 07:11:22 -0800759 verts[0].fOffset = SkPoint::Make(-1, -1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000760 verts[0].fOuterRadius = outerRadius;
761 verts[0].fInnerRadius = innerRadius;
762
joshualitt5ead6da2014-10-22 16:00:29 -0700763 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
bsalomonce1c8862014-12-15 07:11:22 -0800764 verts[1].fOffset = SkPoint::Make(-1, 1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000765 verts[1].fOuterRadius = outerRadius;
766 verts[1].fInnerRadius = innerRadius;
767
joshualitt5ead6da2014-10-22 16:00:29 -0700768 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
bsalomonce1c8862014-12-15 07:11:22 -0800769 verts[2].fOffset = SkPoint::Make(1, 1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000770 verts[2].fOuterRadius = outerRadius;
771 verts[2].fInnerRadius = innerRadius;
772
joshualitt5ead6da2014-10-22 16:00:29 -0700773 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
bsalomonce1c8862014-12-15 07:11:22 -0800774 verts[3].fOffset = SkPoint::Make(1, -1);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000775 verts[3].fOuterRadius = outerRadius;
776 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000777
joshualitt5531d512014-12-17 15:50:11 -0800778 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -0800779 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -0700780 target->resetIndexSource();
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000781}
782
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000783///////////////////////////////////////////////////////////////////////////////
784
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000785bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800786 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800787 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800788 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000789 bool useCoverageAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000790 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -0800791 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000792#ifdef SK_DEBUG
793 {
794 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -0800795 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000796 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000797 }
798#endif
799
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000800 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000801 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800802 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000803 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
804 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -0800805 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
806 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
807 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
808 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000809
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000810 // do (potentially) anisotropic mapping of stroke
811 SkVector scaledStroke;
812 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -0800813 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
814 viewMatrix[SkMatrix::kMSkewY]));
815 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
816 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000817
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000818 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000819 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
820 SkStrokeRec::kHairline_Style == style;
821 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000822
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000823 SkScalar innerXRadius = 0;
824 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000825 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000826 if (SkScalarNearlyZero(scaledStroke.length())) {
827 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
828 } else {
829 scaledStroke.scale(SK_ScalarHalf);
830 }
831
832 // we only handle thick strokes for near-circular ellipses
833 if (scaledStroke.length() > SK_ScalarHalf &&
834 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
835 return false;
836 }
837
838 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
839 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
840 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
841 return false;
842 }
843
844 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000845 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000846 innerXRadius = xRadius - scaledStroke.fX;
847 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000848 }
849
850 xRadius += scaledStroke.fX;
851 yRadius += scaledStroke.fY;
852 }
853
joshualittd27f73e2014-12-29 07:43:36 -0800854 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -0800855 if (!viewMatrix.invert(&invert)) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000856 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000857 }
858
joshualitt56995b52014-12-11 15:44:02 -0800859 SkAutoTUnref<GrGeometryProcessor> gp(
joshualitt8059eb92014-12-29 15:10:07 -0800860 EllipseEdgeEffect::Create(color,
861 isStrokeOnly && innerXRadius > 0 && innerYRadius > 0,
joshualittd27f73e2014-12-29 07:43:36 -0800862 invert));
joshualitt2dd1ae02014-12-03 06:24:10 -0800863
864 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
865 SkASSERT(gp->getVertexStride() == sizeof(EllipseVertex));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000866 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -0700867 SkDebugf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000868 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000869 }
870
871 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
872
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000873 // Compute the reciprocals of the radii here to save time in the shader
874 SkScalar xRadRecip = SkScalarInvert(xRadius);
875 SkScalar yRadRecip = SkScalarInvert(yRadius);
876 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
877 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000878
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000879 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000880 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000881 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000882 xRadius += SK_ScalarHalf;
883 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000884
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000885 SkRect bounds = SkRect::MakeLTRB(
886 center.fX - xRadius,
887 center.fY - yRadius,
888 center.fX + xRadius,
889 center.fY + yRadius
890 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000891
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000892 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000893 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
894 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
895 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000896
joshualitt5ead6da2014-10-22 16:00:29 -0700897 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
898 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000899 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
900 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000901
joshualitt5ead6da2014-10-22 16:00:29 -0700902 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
903 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000904 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
905 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000906
joshualitt5ead6da2014-10-22 16:00:29 -0700907 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
908 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000909 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
910 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000911
joshualitt5531d512014-12-17 15:50:11 -0800912 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -0800913 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -0700914 target->resetIndexSource();
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000915
916 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000917}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000918
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000919bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
joshualitt9853cce2014-11-17 14:22:48 -0800920 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -0800921 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800922 const SkMatrix& viewMatrix,
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000923 bool useCoverageAA,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000924 const SkRect& ellipse,
joshualitt8059eb92014-12-29 15:10:07 -0800925 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000926 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000927 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000928 SkScalar yRadius = SkScalarHalf(ellipse.height());
929
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000930 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000931 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000932 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000933 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000934 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
935
936 SkScalar innerXRadius = 0;
937 SkScalar innerYRadius = 0;
938 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
939 SkScalar strokeWidth = stroke.getWidth();
940
941 if (SkScalarNearlyZero(strokeWidth)) {
942 strokeWidth = SK_ScalarHalf;
943 } else {
944 strokeWidth *= SK_ScalarHalf;
945 }
946
947 // we only handle thick strokes for near-circular ellipses
948 if (strokeWidth > SK_ScalarHalf &&
949 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
950 return false;
951 }
952
953 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
954 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
955 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
956 return false;
957 }
958
959 // set inner radius (if needed)
960 if (SkStrokeRec::kStroke_Style == style) {
961 innerXRadius = xRadius - strokeWidth;
962 innerYRadius = yRadius - strokeWidth;
963 }
964
965 xRadius += strokeWidth;
966 yRadius += strokeWidth;
967 }
968 if (DIEllipseEdgeEffect::kStroke == mode) {
969 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
970 DIEllipseEdgeEffect::kFill;
971 }
972 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
973 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
974
joshualitt8059eb92014-12-29 15:10:07 -0800975 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(color, viewMatrix, mode));
joshualitt2dd1ae02014-12-03 06:24:10 -0800976
977 GrDrawTarget::AutoReleaseGeometry geo(target, 4, gp->getVertexStride(), 0);
978 SkASSERT(gp->getVertexStride() == sizeof(DIEllipseVertex));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000979 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -0700980 SkDebugf("Failed to get space for vertices!\n");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000981 return false;
982 }
983
984 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
985
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000986 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -0800987 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
988 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
989 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
990 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000991 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
992 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
993 // This adjusts the "radius" to include the half-pixel border
994 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
995 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
996
997 SkRect bounds = SkRect::MakeLTRB(
998 center.fX - xRadius - geoDx,
999 center.fY - yRadius - geoDy,
1000 center.fX + xRadius + geoDx,
1001 center.fY + yRadius + geoDy
1002 );
1003
1004 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1005 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1006 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1007
joshualitt5ead6da2014-10-22 16:00:29 -07001008 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1009 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1010 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001011
joshualitt5ead6da2014-10-22 16:00:29 -07001012 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1013 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1014 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001015
joshualitt5ead6da2014-10-22 16:00:29 -07001016 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1017 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1018 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001019
joshualitt5531d512014-12-17 15:50:11 -08001020 target->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
joshualitt56995b52014-12-11 15:44:02 -08001021 target->drawIndexedInstances(drawState, gp, kTriangles_GrPrimitiveType, 1, 4, 6, &bounds);
joshualitt5ead6da2014-10-22 16:00:29 -07001022 target->resetIndexSource();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001023
1024 return true;
1025}
1026
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001027///////////////////////////////////////////////////////////////////////////////
1028
1029static const uint16_t gRRectIndices[] = {
1030 // corners
1031 0, 1, 5, 0, 5, 4,
1032 2, 3, 7, 2, 7, 6,
1033 8, 9, 13, 8, 13, 12,
1034 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001035
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001036 // edges
1037 1, 2, 6, 1, 6, 5,
1038 4, 5, 9, 4, 9, 8,
1039 6, 7, 11, 6, 11, 10,
1040 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001041
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001042 // center
1043 // we place this at the end so that we can ignore these indices when rendering stroke-only
1044 5, 6, 10, 5, 10, 9
1045};
1046
joshualitt5ead6da2014-10-22 16:00:29 -07001047static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1048static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1049static const int kVertsPerRRect = 16;
1050static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001051
joshualitt5531d512014-12-17 15:50:11 -08001052GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(bool isStrokeOnly) {
joshualitt5ead6da2014-10-22 16:00:29 -07001053 if (isStrokeOnly) {
1054 if (NULL == fStrokeRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001055 fStrokeRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1056 kIndicesPerStrokeRRect,
1057 kNumRRectsInIndexBuffer,
1058 kVertsPerRRect);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001059 }
joshualitt5ead6da2014-10-22 16:00:29 -07001060 return fStrokeRRectIndexBuffer;
1061 } else {
1062 if (NULL == fRRectIndexBuffer) {
joshualitt5531d512014-12-17 15:50:11 -08001063 fRRectIndexBuffer = fGpu->createInstancedIndexBuffer(gRRectIndices,
1064 kIndicesPerRRect,
1065 kNumRRectsInIndexBuffer,
1066 kVertsPerRRect);
joshualitt5ead6da2014-10-22 16:00:29 -07001067 }
1068 return fRRectIndexBuffer;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001069 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001070}
1071
joshualitt9853cce2014-11-17 14:22:48 -08001072bool GrOvalRenderer::drawDRRect(GrDrawTarget* target,
1073 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -08001074 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001075 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001076 bool useAA,
1077 const SkRRect& origOuter,
1078 const SkRRect& origInner) {
bsalomon8af05232014-06-03 06:34:58 -07001079 bool applyAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -08001080 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -08001081 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001082 GrDrawState::AutoRestoreEffects are;
1083 if (!origInner.isEmpty()) {
1084 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001085 if (!viewMatrix.isIdentity()) {
1086 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001087 return false;
1088 }
1089 }
joshualittb0a8a372014-09-23 09:50:21 -07001090 GrPrimitiveEdgeType edgeType = applyAA ?
1091 kInverseFillAA_GrProcessorEdgeType :
1092 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001093 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001094 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
1095 if (NULL == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001096 return false;
1097 }
joshualitt9853cce2014-11-17 14:22:48 -08001098 are.set(drawState);
1099 drawState->addCoverageProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001100 }
1101
1102 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
joshualitt8059eb92014-12-29 15:10:07 -08001103 if (this->drawRRect(target, drawState, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001104 return true;
1105 }
1106
1107 SkASSERT(!origOuter.isEmpty());
1108 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001109 if (!viewMatrix.isIdentity()) {
1110 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001111 return false;
1112 }
1113 }
joshualittb0a8a372014-09-23 09:50:21 -07001114 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
1115 kFillBW_GrProcessorEdgeType;
1116 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
bsalomon8af05232014-06-03 06:34:58 -07001117 if (NULL == effect) {
1118 return false;
1119 }
1120 if (!are.isSet()) {
joshualitt9853cce2014-11-17 14:22:48 -08001121 are.set(drawState);
bsalomon8af05232014-06-03 06:34:58 -07001122 }
joshualittd27f73e2014-12-29 07:43:36 -08001123
1124 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001125 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001126 return false;
1127 }
joshualittd27f73e2014-12-29 07:43:36 -08001128
joshualitt9853cce2014-11-17 14:22:48 -08001129 drawState->addCoverageProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001130 SkRect bounds = outer->getBounds();
1131 if (applyAA) {
1132 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1133 }
joshualitt8059eb92014-12-29 15:10:07 -08001134 target->drawRect(drawState, color, SkMatrix::I(), bounds, NULL, &invert);
bsalomon8af05232014-06-03 06:34:58 -07001135 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001136}
1137
joshualitt9853cce2014-11-17 14:22:48 -08001138bool GrOvalRenderer::drawRRect(GrDrawTarget* target,
1139 GrDrawState* drawState,
joshualitt2e3b3e32014-12-09 13:31:14 -08001140 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001141 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001142 bool useAA,
1143 const SkRRect& rrect,
1144 const SkStrokeRec& stroke) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001145 if (rrect.isOval()) {
joshualitt8059eb92014-12-29 15:10:07 -08001146 return this->drawOval(target, drawState, color, viewMatrix, useAA, rrect.getBounds(),
1147 stroke);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001148 }
1149
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001150 bool useCoverageAA = useAA &&
joshualitt9853cce2014-11-17 14:22:48 -08001151 !drawState->getRenderTarget()->isMultisampled() &&
joshualitt2e3b3e32014-12-09 13:31:14 -08001152 drawState->canUseFracCoveragePrimProc(color, *target->caps());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001153
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00001154 // only anti-aliased rrects for now
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001155 if (!useCoverageAA) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +00001156 return false;
1157 }
1158
joshualitt8059eb92014-12-29 15:10:07 -08001159 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001160 return false;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001161 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001162
1163 // do any matrix crunching before we reset the draw state for device coords
1164 const SkRect& rrectBounds = rrect.getBounds();
1165 SkRect bounds;
joshualitt8059eb92014-12-29 15:10:07 -08001166 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001167
1168 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001169 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1170 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1171 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1172 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001173
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001174 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001175
1176 // do (potentially) anisotropic mapping of stroke
1177 SkVector scaledStroke;
1178 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001179
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001180 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1181 SkStrokeRec::kHairline_Style == style;
1182 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1183
1184 if (hasStroke) {
1185 if (SkStrokeRec::kHairline_Style == style) {
1186 scaledStroke.set(1, 1);
1187 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001188 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1189 viewMatrix[SkMatrix::kMSkewY]));
1190 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1191 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001192 }
1193
1194 // if half of strokewidth is greater than radius, we don't handle that right now
1195 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
1196 return false;
1197 }
1198 }
1199
1200 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1201 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1202 // patch will have fractional coverage. This only matters when the interior is actually filled.
1203 // We could consider falling back to rect rendering here, since a tiny radius is
1204 // indistinguishable from a square corner.
1205 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001206 return false;
1207 }
1208
1209 // reset to device coordinates
joshualittd27f73e2014-12-29 07:43:36 -08001210 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001211 if (!viewMatrix.invert(&invert)) {
joshualittd27f73e2014-12-29 07:43:36 -08001212 SkDebugf("Failed to invert\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001213 return false;
1214 }
1215
joshualitt5531d512014-12-17 15:50:11 -08001216 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(isStrokeOnly);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001217 if (NULL == indexBuffer) {
tfarina38406c82014-10-31 07:11:12 -07001218 SkDebugf("Failed to create index buffer!\n");
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001219 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001220 }
1221
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001222 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001223 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001224 SkScalar innerRadius = 0.0f;
1225 SkScalar outerRadius = xRadius;
1226 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001227 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001228 if (SkScalarNearlyZero(scaledStroke.fX)) {
1229 halfWidth = SK_ScalarHalf;
1230 } else {
1231 halfWidth = SkScalarHalf(scaledStroke.fX);
1232 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001233
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001234 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001235 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001236 }
1237 outerRadius += halfWidth;
1238 bounds.outset(halfWidth, halfWidth);
1239 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001240
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001241 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001242
joshualitt8059eb92014-12-29 15:10:07 -08001243 SkAutoTUnref<GrGeometryProcessor> effect(CircleEdgeEffect::Create(color,
1244 isStrokeOnly,
joshualittd27f73e2014-12-29 07:43:36 -08001245 invert));
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001246
joshualitt2dd1ae02014-12-03 06:24:10 -08001247 GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
1248 SkASSERT(effect->getVertexStride() == sizeof(CircleVertex));
1249 if (!geo.succeeded()) {
1250 SkDebugf("Failed to get space for vertices!\n");
1251 return false;
1252 }
1253 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
1254
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001255 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001256 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1257 // Second, the outer radius is used to compute the verts of the bounding box that is
1258 // rendered and the outset ensures the box will cover all partially covered by the rrect
1259 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001260 outerRadius += SK_ScalarHalf;
1261 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001262
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001263 // Expand the rect so all the pixels will be captured.
1264 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001265
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001266 SkScalar yCoords[4] = {
1267 bounds.fTop,
1268 bounds.fTop + outerRadius,
1269 bounds.fBottom - outerRadius,
1270 bounds.fBottom
1271 };
bsalomonce1c8862014-12-15 07:11:22 -08001272 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1273 // The inner radius in the vertex data must be specified in normalized space.
1274 innerRadius = innerRadius / outerRadius;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001275 for (int i = 0; i < 4; ++i) {
1276 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
bsalomonce1c8862014-12-15 07:11:22 -08001277 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001278 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.fLeft + outerRadius, yCoords[i]);
1283 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1284 verts->fOuterRadius = outerRadius;
1285 verts->fInnerRadius = innerRadius;
1286 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001287
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001288 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1289 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1290 verts->fOuterRadius = outerRadius;
1291 verts->fInnerRadius = innerRadius;
1292 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001293
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001294 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
bsalomonce1c8862014-12-15 07:11:22 -08001295 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001296 verts->fOuterRadius = outerRadius;
1297 verts->fInnerRadius = innerRadius;
1298 verts++;
1299 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001300
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001301 // drop out the middle quad if we're stroked
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001302 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1303 SK_ARRAY_COUNT(gRRectIndices);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001304 target->setIndexSourceToBuffer(indexBuffer);
joshualitt56995b52014-12-11 15:44:02 -08001305 target->drawIndexedInstances(drawState, effect, kTriangles_GrPrimitiveType, 1, 16, indexCnt,
joshualitt9853cce2014-11-17 14:22:48 -08001306 &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001307
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001308 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001309 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001310 SkScalar innerXRadius = 0.0f;
1311 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001312 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001313 if (SkScalarNearlyZero(scaledStroke.length())) {
1314 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1315 } else {
1316 scaledStroke.scale(SK_ScalarHalf);
1317 }
1318
1319 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001320 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001321 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1322 return false;
1323 }
1324
1325 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1326 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1327 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1328 return false;
1329 }
1330
1331 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001332 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001333 innerXRadius = xRadius - scaledStroke.fX;
1334 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001335 }
1336
1337 xRadius += scaledStroke.fX;
1338 yRadius += scaledStroke.fY;
1339 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1340 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001341
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001342 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001343
joshualitt8059eb92014-12-29 15:10:07 -08001344 SkAutoTUnref<GrGeometryProcessor> effect(EllipseEdgeEffect::Create(color,
1345 isStrokeOnly,
joshualittd27f73e2014-12-29 07:43:36 -08001346 invert));
joshualitt2dd1ae02014-12-03 06:24:10 -08001347
1348 GrDrawTarget::AutoReleaseGeometry geo(target, 16, effect->getVertexStride(), 0);
1349 SkASSERT(effect->getVertexStride() == sizeof(EllipseVertex));
jvanverth@google.come3647412013-05-08 15:31:43 +00001350 if (!geo.succeeded()) {
tfarina38406c82014-10-31 07:11:12 -07001351 SkDebugf("Failed to get space for vertices!\n");
jvanverth@google.come3647412013-05-08 15:31:43 +00001352 return false;
1353 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001354
joshualitt2dd1ae02014-12-03 06:24:10 -08001355 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001356
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001357 // Compute the reciprocals of the radii here to save time in the shader
1358 SkScalar xRadRecip = SkScalarInvert(xRadius);
1359 SkScalar yRadRecip = SkScalarInvert(yRadius);
1360 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1361 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001362
1363 // Extend the radii out half a pixel to antialias.
1364 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1365 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001366
1367 // Expand the rect so all the pixels will be captured.
1368 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1369
1370 SkScalar yCoords[4] = {
1371 bounds.fTop,
1372 bounds.fTop + yOuterRadius,
1373 bounds.fBottom - yOuterRadius,
1374 bounds.fBottom
1375 };
1376 SkScalar yOuterOffsets[4] = {
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001377 yOuterRadius,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001378 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1379 SK_ScalarNearlyZero,
1380 yOuterRadius
1381 };
1382
1383 for (int i = 0; i < 4; ++i) {
1384 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001385 verts->fOffset = SkPoint::Make(xOuterRadius, 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.fLeft + xOuterRadius, yCoords[i]);
1391 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, 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
1396 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1397 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001398 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1399 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001400 verts++;
1401
1402 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1403 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001404 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1405 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001406 verts++;
1407 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001408
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001409 // drop out the middle quad if we're stroked
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001410 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 :
1411 SK_ARRAY_COUNT(gRRectIndices);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001412 target->setIndexSourceToBuffer(indexBuffer);
joshualitt56995b52014-12-11 15:44:02 -08001413 target->drawIndexedInstances(drawState, effect, kTriangles_GrPrimitiveType, 1, 16, indexCnt,
joshualitt9853cce2014-11-17 14:22:48 -08001414 &bounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001415 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001416
joshualitt5ead6da2014-10-22 16:00:29 -07001417 target->resetIndexSource();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001418 return true;
1419}