blob: c6986920ff53b86daa21804291c824308d0e4ba3 [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
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000309void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000310 const GrPaint& paint,
311 const GrRect& circle,
312 const SkStrokeRec& stroke)
313{
314 GrDrawState* drawState = target->drawState();
315
316 const SkMatrix& vm = drawState->getViewMatrix();
317 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
318 vm.mapPoints(&center, 1);
319 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
320 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
321
322 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
323 if (!adcd.succeeded()) {
324 return;
325 }
326
327 // position + edge
328 static const GrVertexAttrib kVertexAttribs[] = {
jvanverth@google.com054ae992013-04-01 20:06:51 +0000329 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
330 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000331 };
332 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000333 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
334
335 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
336 if (!geo.succeeded()) {
337 GrPrintf("Failed to get space for vertices!\n");
338 return;
339 }
340
341 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
342
343 SkStrokeRec::Style style = stroke.getStyle();
344 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
345 enum {
346 // the edge effects share this stage with glyph rendering
347 // (kGlyphMaskStage in GrTextContext) && SW path rendering
348 // (kPathMaskStage in GrSWMaskHelper)
349 kEdgeEffectStage = GrPaint::kTotalStages,
350 };
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000351
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000353 static const int kCircleEdgeAttrIndex = 1;
354 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
355
356 SkScalar innerRadius = 0.0f;
357 SkScalar outerRadius = radius;
358 SkScalar halfWidth = 0;
359 if (style != SkStrokeRec::kFill_Style) {
360 if (SkScalarNearlyZero(strokeWidth)) {
361 halfWidth = SK_ScalarHalf;
362 } else {
363 halfWidth = SkScalarHalf(strokeWidth);
364 }
365
366 outerRadius += halfWidth;
367 if (isStroked) {
368 innerRadius = SkMaxScalar(0, radius - halfWidth);
369 }
370 }
371
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000372 // The radii are outset for two reasons. First, it allows the shader to simply perform
373 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
374 // verts of the bounding box that is rendered and the outset ensures the box will cover all
375 // pixels partially covered by the circle.
376 outerRadius += SK_ScalarHalf;
377 innerRadius -= SK_ScalarHalf;
378
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000379 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000380 center.fX - outerRadius,
381 center.fY - outerRadius,
382 center.fX + outerRadius,
383 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000384 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000385
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000386 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000387 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
388 verts[0].fOuterRadius = outerRadius;
389 verts[0].fInnerRadius = innerRadius;
390
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000391 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000392 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000393 verts[1].fOuterRadius = outerRadius;
394 verts[1].fInnerRadius = innerRadius;
395
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000396 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000397 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
398 verts[2].fOuterRadius = outerRadius;
399 verts[2].fInnerRadius = innerRadius;
400
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000401 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000402 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
403 verts[3].fOuterRadius = outerRadius;
404 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000405
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000406 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000407}
408
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000409bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000410 const GrPaint& paint,
411 const GrRect& ellipse,
412 const SkStrokeRec& stroke)
413{
414 GrDrawState* drawState = target->drawState();
415#ifdef SK_DEBUG
416 {
417 // we should have checked for this previously
418 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
419 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
420 }
421#endif
422
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000423 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000424 const SkMatrix& vm = drawState->getViewMatrix();
425 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
426 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000427 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
428 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000429 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000430 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000431 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000432 vm[SkMatrix::kMScaleY]*ellipseYRadius);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000433 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
434 return false;
435 }
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000436
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000437 // do (potentially) anisotropic mapping of stroke
438 SkVector scaledStroke;
439 SkScalar strokeWidth = stroke.getWidth();
440 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
441 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
442
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000443 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
444 if (!adcd.succeeded()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000445 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000446 }
447
448 // position + edge
449 static const GrVertexAttrib kVertexAttribs[] = {
jvanverth@google.com054ae992013-04-01 20:06:51 +0000450 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
451 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
452 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000453 };
454 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000455 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
456
457 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
458 if (!geo.succeeded()) {
459 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000460 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000461 }
462
463 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
464
465 SkStrokeRec::Style style = stroke.getStyle();
466 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
467 enum {
468 // the edge effects share this stage with glyph rendering
469 // (kGlyphMaskStage in GrTextContext) && SW path rendering
470 // (kPathMaskStage in GrSWMaskHelper)
471 kEdgeEffectStage = GrPaint::kTotalStages,
472 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000473
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000474 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000475 static const int kEllipseCenterAttrIndex = 1;
476 static const int kEllipseEdgeAttrIndex = 2;
477 drawState->setEffect(kEdgeEffectStage, effect,
478 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
479
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000480 SkScalar innerXRadius = 0.0f;
481 SkScalar innerRatio = 1.0f;
482
483 if (SkStrokeRec::kFill_Style != style) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000484
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000485
486 if (SkScalarNearlyZero(scaledStroke.length())) {
487 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
488 } else {
489 scaledStroke.scale(0.5f);
490 }
491
492 // this is legit only if scale & translation (which should be the case at the moment)
493 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
494 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
495 if (innerYRadius > SK_ScalarNearlyZero) {
496 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
497 innerRatio = innerXRadius/innerYRadius;
498 }
499 }
500 xRadius += scaledStroke.fX;
501 yRadius += scaledStroke.fY;
502 }
503
504 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
505
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000506 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000507 // This will also expand the rect so all the pixels will be captured.
508 xRadius += SK_ScalarHalf;
509 yRadius += SK_ScalarHalf;
510 innerXRadius -= SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000511
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000512 SkRect bounds = SkRect::MakeLTRB(
513 center.fX - xRadius,
514 center.fY - yRadius,
515 center.fX + xRadius,
516 center.fY + yRadius
517 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000518
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000519 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up
520 // with a circle equation which can be checked quickly in the shader. We need one offset for
521 // 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 +0000522 // non-uniform strokes.
523 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
524 verts[0].fOuterXRadius = xRadius;
525 verts[0].fInnerXRadius = innerXRadius;
526 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius);
527 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius);
528
529 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
530 verts[1].fOuterXRadius = xRadius;
531 verts[1].fInnerXRadius = innerXRadius;
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000532 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
533 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000534
535 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
536 verts[2].fOuterXRadius = xRadius;
537 verts[2].fInnerXRadius = innerXRadius;
538 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius);
539 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius);
540
541 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
542 verts[3].fOuterXRadius = xRadius;
543 verts[3].fInnerXRadius = innerXRadius;
544 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius);
545 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000546
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000547 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000548
549 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000550}