blob: 4b795287afdb92382779a088a28b9ef07ed81f3c [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) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000054 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
55 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000056
57 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000058 gCircleStrokeEdge->ref();
59 return gCircleStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000061 gCircleFillEdge->ref();
62 return gCircleFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000063 }
64 }
65
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +000066 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000067 uint32_t* validFlags) const SK_OVERRIDE {
68 *validFlags = 0;
69 }
70
71 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
72 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
73 }
74
75 virtual ~CircleEdgeEffect() {}
76
77 static const char* Name() { return "CircleEdge"; }
78
79 inline bool isStroked() const { return fStroke; }
80
81 class GLEffect : public GrGLEffect {
82 public:
83 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
84 : INHERITED (factory) {}
85
86 virtual void emitCode(GrGLShaderBuilder* builder,
87 const GrDrawEffect& drawEffect,
88 EffectKey key,
89 const char* outputColor,
90 const char* inputColor,
91 const TextureSamplerArray& samplers) SK_OVERRIDE {
92 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
93 const char *vsName, *fsName;
94 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
95
96 const SkString* attrName =
97 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
98 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
99
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000100 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000101 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
102 if (circleEffect.isStroked()) {
103 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
104 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
105 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000106
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000108 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
110 }
111
112 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
113 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
114
115 return circleEffect.isStroked() ? 0x1 : 0x0;
116 }
117
118 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
119
120 private:
121 typedef GrGLEffect INHERITED;
122 };
123
124
125private:
126 CircleEdgeEffect(bool stroke) : GrEffect() {
127 this->addVertexAttrib(kVec4f_GrSLType);
128 fStroke = stroke;
129 }
130
131 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
132 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
133 return cee.fStroke == fStroke;
134 }
135
136 bool fStroke;
137
138 GR_DECLARE_EFFECT_TEST;
139
140 typedef GrEffect INHERITED;
141};
142
143GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
144
145GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
146 GrContext* context,
147 const GrDrawTargetCaps&,
148 GrTexture* textures[]) {
149 return CircleEdgeEffect::Create(random->nextBool());
150}
151
152///////////////////////////////////////////////////////////////////////////////
153
154/**
155 * 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 +0000156 * ellipse, specified as outer and inner radii, and outer and inner offsets from center.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000157 */
158
159class EllipseEdgeEffect : public GrEffect {
160public:
161 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000162 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
163 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000164
165 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000166 gEllipseStrokeEdge->ref();
167 return gEllipseStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000168 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000169 gEllipseFillEdge->ref();
170 return gEllipseFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
172 }
173
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000174 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000175 uint32_t* validFlags) const SK_OVERRIDE {
176 *validFlags = 0;
177 }
178
179 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
180 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
181 }
182
183 virtual ~EllipseEdgeEffect() {}
184
185 static const char* Name() { return "EllipseEdge"; }
186
187 inline bool isStroked() const { return fStroke; }
188
189 class GLEffect : public GrGLEffect {
190 public:
191 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
192 : INHERITED (factory) {}
193
194 virtual void emitCode(GrGLShaderBuilder* builder,
195 const GrDrawEffect& drawEffect,
196 EffectKey key,
197 const char* outputColor,
198 const char* inputColor,
199 const TextureSamplerArray& samplers) SK_OVERRIDE {
200 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
201
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000202 const char *vsRadiiName, *fsRadiiName;
203 const char *vsOffsetsName, *fsOffsetsName;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000205 builder->addVarying(kVec2f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000206 const SkString* attr0Name =
207 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000208 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr0Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000209
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000210 builder->addVarying(kVec4f_GrSLType, "EllipseOffsets", &vsOffsetsName, &fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211 const SkString* attr1Name =
212 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000213 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetsName, attr1Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000215 // get length of offset
216 builder->fsCodeAppendf("\tfloat dOuter = length(%s.xy);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217 // compare outer lengths against xOuterRadius
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000218 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n",
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000219 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220
221 if (ellipseEffect.isStroked()) {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000222 builder->fsCodeAppendf("\tfloat dInner = length(%s.zw);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223
224 // compare inner lengths against xInnerRadius
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000225 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.y, 0.0, 1.0);\n",
226 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
228 }
229
230 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000231 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000232 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233 }
234
235 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
236 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
237
238 return ellipseEffect.isStroked() ? 0x1 : 0x0;
239 }
240
241 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
242 }
243
244 private:
245 typedef GrGLEffect INHERITED;
246 };
247
248private:
249 EllipseEdgeEffect(bool stroke) : GrEffect() {
250 this->addVertexAttrib(kVec2f_GrSLType);
251 this->addVertexAttrib(kVec4f_GrSLType);
252 fStroke = stroke;
253 }
254
255 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
256 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
257 return eee.fStroke == fStroke;
258 }
259
260 bool fStroke;
261
262 GR_DECLARE_EFFECT_TEST;
263
264 typedef GrEffect INHERITED;
265};
266
267GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
268
269GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
270 GrContext* context,
271 const GrDrawTargetCaps&,
272 GrTexture* textures[]) {
273 return EllipseEdgeEffect::Create(random->nextBool());
274}
275
276///////////////////////////////////////////////////////////////////////////////
277
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000278bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000279 const GrRect& oval, const SkStrokeRec& stroke)
280{
281 if (!paint.isAntiAlias()) {
282 return false;
283 }
284
285 const SkMatrix& vm = context->getMatrix();
286
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000287 // we can draw circles
288 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000289 && circle_stays_circle(vm)) {
290 drawCircle(target, paint, oval, stroke);
291
292 // and axis-aligned ellipses only
293 } else if (vm.rectStaysRect()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000294 return drawEllipse(target, paint, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000295
296 } else {
297 return false;
298 }
299
300 return true;
301}
302
robertphillips@google.com42903302013-04-20 12:26:07 +0000303namespace {
304
305// position + edge
306extern const GrVertexAttrib gCircleVertexAttribs[] = {
307 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
308 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
309};
310
311};
312
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000313void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000314 const GrPaint& paint,
315 const GrRect& circle,
316 const SkStrokeRec& stroke)
317{
318 GrDrawState* drawState = target->drawState();
319
320 const SkMatrix& vm = drawState->getViewMatrix();
321 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
322 vm.mapPoints(&center, 1);
323 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
324 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
325
326 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
327 if (!adcd.succeeded()) {
328 return;
329 }
330
robertphillips@google.com42903302013-04-20 12:26:07 +0000331 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000332 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
333
334 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
335 if (!geo.succeeded()) {
336 GrPrintf("Failed to get space for vertices!\n");
337 return;
338 }
339
340 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
341
342 SkStrokeRec::Style style = stroke.getStyle();
343 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
344 enum {
345 // the edge effects share this stage with glyph rendering
346 // (kGlyphMaskStage in GrTextContext) && SW path rendering
347 // (kPathMaskStage in GrSWMaskHelper)
348 kEdgeEffectStage = GrPaint::kTotalStages,
349 };
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000350
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000351 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000352 static const int kCircleEdgeAttrIndex = 1;
353 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
354
355 SkScalar innerRadius = 0.0f;
356 SkScalar outerRadius = radius;
357 SkScalar halfWidth = 0;
358 if (style != SkStrokeRec::kFill_Style) {
359 if (SkScalarNearlyZero(strokeWidth)) {
360 halfWidth = SK_ScalarHalf;
361 } else {
362 halfWidth = SkScalarHalf(strokeWidth);
363 }
364
365 outerRadius += halfWidth;
366 if (isStroked) {
367 innerRadius = SkMaxScalar(0, radius - halfWidth);
368 }
369 }
370
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000371 // The radii are outset for two reasons. First, it allows the shader to simply perform
372 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
373 // verts of the bounding box that is rendered and the outset ensures the box will cover all
374 // pixels partially covered by the circle.
375 outerRadius += SK_ScalarHalf;
376 innerRadius -= SK_ScalarHalf;
377
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000378 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000379 center.fX - outerRadius,
380 center.fY - outerRadius,
381 center.fX + outerRadius,
382 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000383 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000384
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000385 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000386 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
387 verts[0].fOuterRadius = outerRadius;
388 verts[0].fInnerRadius = innerRadius;
389
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000390 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000391 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000392 verts[1].fOuterRadius = outerRadius;
393 verts[1].fInnerRadius = innerRadius;
394
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000395 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000396 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
397 verts[2].fOuterRadius = outerRadius;
398 verts[2].fInnerRadius = innerRadius;
399
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000400 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000401 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
402 verts[3].fOuterRadius = outerRadius;
403 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000404
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000405 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000406}
407
robertphillips@google.com42903302013-04-20 12:26:07 +0000408namespace {
409
410// position + edge
411extern const GrVertexAttrib gEllipseVertexAttribs[] = {
412 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
413 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
414 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
415};
416
417};
418
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000419bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000420 const GrPaint& paint,
421 const GrRect& ellipse,
422 const SkStrokeRec& stroke)
423{
424 GrDrawState* drawState = target->drawState();
425#ifdef SK_DEBUG
426 {
427 // we should have checked for this previously
428 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
429 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
430 }
431#endif
432
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000433 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000434 const SkMatrix& vm = drawState->getViewMatrix();
435 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
436 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000437 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
438 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000439 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000440 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000441 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000442 vm[SkMatrix::kMScaleY]*ellipseYRadius);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000443 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
444 return false;
445 }
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000446
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000447 // do (potentially) anisotropic mapping of stroke
448 SkVector scaledStroke;
449 SkScalar strokeWidth = stroke.getWidth();
450 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
451 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
452
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000453 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
454 if (!adcd.succeeded()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000455 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000456 }
457
robertphillips@google.com42903302013-04-20 12:26:07 +0000458 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000459 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
460
461 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
462 if (!geo.succeeded()) {
463 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000464 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000465 }
466
467 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
468
469 SkStrokeRec::Style style = stroke.getStyle();
470 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
471 enum {
472 // the edge effects share this stage with glyph rendering
473 // (kGlyphMaskStage in GrTextContext) && SW path rendering
474 // (kPathMaskStage in GrSWMaskHelper)
475 kEdgeEffectStage = GrPaint::kTotalStages,
476 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000477
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000478 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000479 static const int kEllipseCenterAttrIndex = 1;
480 static const int kEllipseEdgeAttrIndex = 2;
481 drawState->setEffect(kEdgeEffectStage, effect,
482 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
483
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000484 SkScalar innerXRadius = 0.0f;
485 SkScalar innerRatio = 1.0f;
486
487 if (SkStrokeRec::kFill_Style != style) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000488
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000489
490 if (SkScalarNearlyZero(scaledStroke.length())) {
491 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
492 } else {
493 scaledStroke.scale(0.5f);
494 }
495
496 // this is legit only if scale & translation (which should be the case at the moment)
497 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
498 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
499 if (innerYRadius > SK_ScalarNearlyZero) {
500 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
501 innerRatio = innerXRadius/innerYRadius;
502 }
503 }
504 xRadius += scaledStroke.fX;
505 yRadius += scaledStroke.fY;
506 }
507
508 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
509
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000510 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000511 // This will also expand the rect so all the pixels will be captured.
512 xRadius += SK_ScalarHalf;
513 yRadius += SK_ScalarHalf;
514 innerXRadius -= SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000515
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000516 SkRect bounds = SkRect::MakeLTRB(
517 center.fX - xRadius,
518 center.fY - yRadius,
519 center.fX + xRadius,
520 center.fY + yRadius
521 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000522
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000523 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up
524 // with a circle equation which can be checked quickly in the shader. We need one offset for
525 // 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 +0000526 // non-uniform strokes.
527 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
528 verts[0].fOuterXRadius = xRadius;
529 verts[0].fInnerXRadius = innerXRadius;
530 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius);
531 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius);
532
533 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
534 verts[1].fOuterXRadius = xRadius;
535 verts[1].fInnerXRadius = innerXRadius;
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000536 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
537 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000538
539 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
540 verts[2].fOuterXRadius = xRadius;
541 verts[2].fInnerXRadius = innerXRadius;
542 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius);
543 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius);
544
545 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
546 verts[3].fOuterXRadius = xRadius;
547 verts[3].fInnerXRadius = innerXRadius;
548 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius);
549 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000550
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000551 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000552
553 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000554}