blob: f217262536a70e112d53cc9b750b85f311b911b1 [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
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000010#include "GrEffect.h"
11#include "gl/GrGLEffect.h"
12#include "gl/GrGLSL.h"
13#include "GrTBackendEffectFactory.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014
15#include "GrDrawState.h"
16#include "GrDrawTarget.h"
17#include "SkStrokeRec.h"
18
19SK_DEFINE_INST_COUNT(GrOvalRenderer)
20
21namespace {
22
23struct CircleVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000024 GrPoint fPos;
25 GrPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000026 SkScalar fOuterRadius;
27 SkScalar fInnerRadius;
28};
29
30struct EllipseVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000031 GrPoint fPos;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032 SkScalar fOuterXRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033 SkScalar fInnerXRadius;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000034 GrPoint fOuterOffset;
35 GrPoint fInnerOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000036};
37
38inline bool circle_stays_circle(const SkMatrix& m) {
39 return m.isSimilarity();
40}
41
42}
43
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000044///////////////////////////////////////////////////////////////////////////////
45
46/**
47 * The output of this effect is a modulation of the input color and coverage for a circle,
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000048 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000049 */
50
51class CircleEdgeEffect : public GrEffect {
52public:
53 static GrEffectRef* Create(bool stroke) {
54 // we go through this so we only have one copy of each effect (stroked/filled)
55 static SkAutoTUnref<GrEffectRef> gCircleStrokeEdgeEffectRef(
56 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (true)))));
57 static SkAutoTUnref<GrEffectRef> gCircleFillEdgeEffectRef(
58 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (false)))));
59
60 if (stroke) {
61 gCircleStrokeEdgeEffectRef.get()->ref();
62 return gCircleStrokeEdgeEffectRef;
63 } else {
64 gCircleFillEdgeEffectRef.get()->ref();
65 return gCircleFillEdgeEffectRef;
66 }
67 }
68
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +000069 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070 uint32_t* validFlags) const SK_OVERRIDE {
71 *validFlags = 0;
72 }
73
74 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
75 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
76 }
77
78 virtual ~CircleEdgeEffect() {}
79
80 static const char* Name() { return "CircleEdge"; }
81
82 inline bool isStroked() const { return fStroke; }
83
84 class GLEffect : public GrGLEffect {
85 public:
86 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
87 : INHERITED (factory) {}
88
89 virtual void emitCode(GrGLShaderBuilder* builder,
90 const GrDrawEffect& drawEffect,
91 EffectKey key,
92 const char* outputColor,
93 const char* inputColor,
94 const TextureSamplerArray& samplers) SK_OVERRIDE {
95 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
96 const char *vsName, *fsName;
97 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
98
99 const SkString* attrName =
100 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
101 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
102
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000103 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
105 if (circleEffect.isStroked()) {
106 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
107 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
108 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000109
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000111 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
113 }
114
115 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
116 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
117
118 return circleEffect.isStroked() ? 0x1 : 0x0;
119 }
120
121 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
122
123 private:
124 typedef GrGLEffect INHERITED;
125 };
126
127
128private:
129 CircleEdgeEffect(bool stroke) : GrEffect() {
130 this->addVertexAttrib(kVec4f_GrSLType);
131 fStroke = stroke;
132 }
133
134 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
135 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
136 return cee.fStroke == fStroke;
137 }
138
139 bool fStroke;
140
141 GR_DECLARE_EFFECT_TEST;
142
143 typedef GrEffect INHERITED;
144};
145
146GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
147
148GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
149 GrContext* context,
150 const GrDrawTargetCaps&,
151 GrTexture* textures[]) {
152 return CircleEdgeEffect::Create(random->nextBool());
153}
154
155///////////////////////////////////////////////////////////////////////////////
156
157/**
158 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000159 * ellipse, specified as outer and inner radii, and outer and inner offsets from center.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000160 */
161
162class EllipseEdgeEffect : public GrEffect {
163public:
164 static GrEffectRef* Create(bool stroke) {
165 // we go through this so we only have one copy of each effect (stroked/filled)
166 static SkAutoTUnref<GrEffectRef> gEllipseStrokeEdgeEffectRef(
167 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (true)))));
168 static SkAutoTUnref<GrEffectRef> gEllipseFillEdgeEffectRef(
169 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (false)))));
170
171 if (stroke) {
172 gEllipseStrokeEdgeEffectRef.get()->ref();
173 return gEllipseStrokeEdgeEffectRef;
174 } else {
175 gEllipseFillEdgeEffectRef.get()->ref();
176 return gEllipseFillEdgeEffectRef;
177 }
178 }
179
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000180 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000181 uint32_t* validFlags) const SK_OVERRIDE {
182 *validFlags = 0;
183 }
184
185 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
186 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
187 }
188
189 virtual ~EllipseEdgeEffect() {}
190
191 static const char* Name() { return "EllipseEdge"; }
192
193 inline bool isStroked() const { return fStroke; }
194
195 class GLEffect : public GrGLEffect {
196 public:
197 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
198 : INHERITED (factory) {}
199
200 virtual void emitCode(GrGLShaderBuilder* builder,
201 const GrDrawEffect& drawEffect,
202 EffectKey key,
203 const char* outputColor,
204 const char* inputColor,
205 const TextureSamplerArray& samplers) SK_OVERRIDE {
206 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
207
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000208 const char *vsRadiiName, *fsRadiiName;
209 const char *vsOffsetsName, *fsOffsetsName;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000210
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000211 builder->addVarying(kVec2f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000212 const SkString* attr0Name =
213 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000214 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr0Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000215
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000216 builder->addVarying(kVec4f_GrSLType, "EllipseOffsets", &vsOffsetsName, &fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217 const SkString* attr1Name =
218 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000219 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetsName, attr1Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000221 // get length of offset
222 builder->fsCodeAppendf("\tfloat dOuter = length(%s.xy);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 // compare outer lengths against xOuterRadius
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000224 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n",
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000225 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000226
227 if (ellipseEffect.isStroked()) {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000228 builder->fsCodeAppendf("\tfloat dInner = length(%s.zw);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000229
230 // compare inner lengths against xInnerRadius
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000231 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.y, 0.0, 1.0);\n",
232 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
234 }
235
236 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000237 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000238 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239 }
240
241 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
242 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
243
244 return ellipseEffect.isStroked() ? 0x1 : 0x0;
245 }
246
247 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
248 }
249
250 private:
251 typedef GrGLEffect INHERITED;
252 };
253
254private:
255 EllipseEdgeEffect(bool stroke) : GrEffect() {
256 this->addVertexAttrib(kVec2f_GrSLType);
257 this->addVertexAttrib(kVec4f_GrSLType);
258 fStroke = stroke;
259 }
260
261 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
262 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
263 return eee.fStroke == fStroke;
264 }
265
266 bool fStroke;
267
268 GR_DECLARE_EFFECT_TEST;
269
270 typedef GrEffect INHERITED;
271};
272
273GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
274
275GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
276 GrContext* context,
277 const GrDrawTargetCaps&,
278 GrTexture* textures[]) {
279 return EllipseEdgeEffect::Create(random->nextBool());
280}
281
282///////////////////////////////////////////////////////////////////////////////
283
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000284bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000285 const GrRect& oval, const SkStrokeRec& stroke)
286{
287 if (!paint.isAntiAlias()) {
288 return false;
289 }
290
291 const SkMatrix& vm = context->getMatrix();
292
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000293 // we can draw circles
294 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000295 && circle_stays_circle(vm)) {
296 drawCircle(target, paint, oval, stroke);
297
298 // and axis-aligned ellipses only
299 } else if (vm.rectStaysRect()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000300 return drawEllipse(target, paint, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000301
302 } else {
303 return false;
304 }
305
306 return true;
307}
308
robertphillips@google.com42903302013-04-20 12:26:07 +0000309namespace {
310
311// position + edge
312extern const GrVertexAttrib gCircleVertexAttribs[] = {
313 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
314 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
315};
316
317};
318
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000319void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000320 const GrPaint& paint,
321 const GrRect& circle,
322 const SkStrokeRec& stroke)
323{
324 GrDrawState* drawState = target->drawState();
325
326 const SkMatrix& vm = drawState->getViewMatrix();
327 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
328 vm.mapPoints(&center, 1);
329 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
330 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
331
332 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
333 if (!adcd.succeeded()) {
334 return;
335 }
336
robertphillips@google.com42903302013-04-20 12:26:07 +0000337 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000338 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
339
340 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
341 if (!geo.succeeded()) {
342 GrPrintf("Failed to get space for vertices!\n");
343 return;
344 }
345
346 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
347
348 SkStrokeRec::Style style = stroke.getStyle();
349 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
350 enum {
351 // the edge effects share this stage with glyph rendering
352 // (kGlyphMaskStage in GrTextContext) && SW path rendering
353 // (kPathMaskStage in GrSWMaskHelper)
354 kEdgeEffectStage = GrPaint::kTotalStages,
355 };
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000356
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000358 static const int kCircleEdgeAttrIndex = 1;
359 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
360
361 SkScalar innerRadius = 0.0f;
362 SkScalar outerRadius = radius;
363 SkScalar halfWidth = 0;
364 if (style != SkStrokeRec::kFill_Style) {
365 if (SkScalarNearlyZero(strokeWidth)) {
366 halfWidth = SK_ScalarHalf;
367 } else {
368 halfWidth = SkScalarHalf(strokeWidth);
369 }
370
371 outerRadius += halfWidth;
372 if (isStroked) {
373 innerRadius = SkMaxScalar(0, radius - halfWidth);
374 }
375 }
376
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000377 // The radii are outset for two reasons. First, it allows the shader to simply perform
378 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
379 // verts of the bounding box that is rendered and the outset ensures the box will cover all
380 // pixels partially covered by the circle.
381 outerRadius += SK_ScalarHalf;
382 innerRadius -= SK_ScalarHalf;
383
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000384 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000385 center.fX - outerRadius,
386 center.fY - outerRadius,
387 center.fX + outerRadius,
388 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000389 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000390
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000391 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000392 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
393 verts[0].fOuterRadius = outerRadius;
394 verts[0].fInnerRadius = innerRadius;
395
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000396 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000397 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000398 verts[1].fOuterRadius = outerRadius;
399 verts[1].fInnerRadius = innerRadius;
400
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000401 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000402 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
403 verts[2].fOuterRadius = outerRadius;
404 verts[2].fInnerRadius = innerRadius;
405
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000406 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000407 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
408 verts[3].fOuterRadius = outerRadius;
409 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000410
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000411 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000412}
413
robertphillips@google.com42903302013-04-20 12:26:07 +0000414namespace {
415
416// position + edge
417extern const GrVertexAttrib gEllipseVertexAttribs[] = {
418 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
419 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
420 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
421};
422
423};
424
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000425bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000426 const GrPaint& paint,
427 const GrRect& ellipse,
428 const SkStrokeRec& stroke)
429{
430 GrDrawState* drawState = target->drawState();
431#ifdef SK_DEBUG
432 {
433 // we should have checked for this previously
434 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
435 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
436 }
437#endif
438
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000439 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000440 const SkMatrix& vm = drawState->getViewMatrix();
441 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
442 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000443 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
444 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000445 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000446 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000447 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000448 vm[SkMatrix::kMScaleY]*ellipseYRadius);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000449 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
450 return false;
451 }
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000452
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000453 // do (potentially) anisotropic mapping of stroke
454 SkVector scaledStroke;
455 SkScalar strokeWidth = stroke.getWidth();
456 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
457 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
458
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000459 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
460 if (!adcd.succeeded()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000461 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000462 }
463
robertphillips@google.com42903302013-04-20 12:26:07 +0000464 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000465 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
466
467 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
468 if (!geo.succeeded()) {
469 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000470 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000471 }
472
473 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
474
475 SkStrokeRec::Style style = stroke.getStyle();
476 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
477 enum {
478 // the edge effects share this stage with glyph rendering
479 // (kGlyphMaskStage in GrTextContext) && SW path rendering
480 // (kPathMaskStage in GrSWMaskHelper)
481 kEdgeEffectStage = GrPaint::kTotalStages,
482 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000483
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000484 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000485 static const int kEllipseCenterAttrIndex = 1;
486 static const int kEllipseEdgeAttrIndex = 2;
487 drawState->setEffect(kEdgeEffectStage, effect,
488 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
489
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000490 SkScalar innerXRadius = 0.0f;
491 SkScalar innerRatio = 1.0f;
492
493 if (SkStrokeRec::kFill_Style != style) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000494
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000495
496 if (SkScalarNearlyZero(scaledStroke.length())) {
497 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
498 } else {
499 scaledStroke.scale(0.5f);
500 }
501
502 // this is legit only if scale & translation (which should be the case at the moment)
503 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
504 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
505 if (innerYRadius > SK_ScalarNearlyZero) {
506 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
507 innerRatio = innerXRadius/innerYRadius;
508 }
509 }
510 xRadius += scaledStroke.fX;
511 yRadius += scaledStroke.fY;
512 }
513
514 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
515
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000516 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000517 // This will also expand the rect so all the pixels will be captured.
518 xRadius += SK_ScalarHalf;
519 yRadius += SK_ScalarHalf;
520 innerXRadius -= SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000521
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000522 SkRect bounds = SkRect::MakeLTRB(
523 center.fX - xRadius,
524 center.fY - yRadius,
525 center.fX + xRadius,
526 center.fY + yRadius
527 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000528
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000529 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up
530 // with a circle equation which can be checked quickly in the shader. We need one offset for
531 // outer and one for inner because they have different scale factors -- otherwise we end up with
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000532 // non-uniform strokes.
533 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
534 verts[0].fOuterXRadius = xRadius;
535 verts[0].fInnerXRadius = innerXRadius;
536 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius);
537 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius);
538
539 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
540 verts[1].fOuterXRadius = xRadius;
541 verts[1].fInnerXRadius = innerXRadius;
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000542 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
543 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000544
545 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
546 verts[2].fOuterXRadius = xRadius;
547 verts[2].fInnerXRadius = innerXRadius;
548 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius);
549 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius);
550
551 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
552 verts[3].fOuterXRadius = xRadius;
553 verts[3].fInnerXRadius = innerXRadius;
554 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius);
555 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000556
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000557 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000558
559 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000560}