blob: 731964c2d7ebf366d922181cf68e5f94c3ed8e29 [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;
111 GrGLSLModulate4f(&modulate, inputColor, "edgeAlpha");
112 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;
237 GrGLSLModulate4f(&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()) {
300 drawEllipse(target, paint, oval, stroke);
301
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);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000392 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
393 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
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000409void 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
423 const SkMatrix& vm = drawState->getViewMatrix();
424 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
425 vm.mapPoints(&center, 1);
426 SkRect xformedRect;
427 vm.mapRect(&xformedRect, ellipse);
428
429 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
430 if (!adcd.succeeded()) {
431 return;
432 }
433
434 // position + edge
435 static const GrVertexAttrib kVertexAttribs[] = {
jvanverth@google.com054ae992013-04-01 20:06:51 +0000436 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
437 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
438 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000439 };
440 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000441 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
442
443 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
444 if (!geo.succeeded()) {
445 GrPrintf("Failed to get space for vertices!\n");
446 return;
447 }
448
449 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
450
451 SkStrokeRec::Style style = stroke.getStyle();
452 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
453 enum {
454 // the edge effects share this stage with glyph rendering
455 // (kGlyphMaskStage in GrTextContext) && SW path rendering
456 // (kPathMaskStage in GrSWMaskHelper)
457 kEdgeEffectStage = GrPaint::kTotalStages,
458 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000459
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000460 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000461 static const int kEllipseCenterAttrIndex = 1;
462 static const int kEllipseEdgeAttrIndex = 2;
463 drawState->setEffect(kEdgeEffectStage, effect,
464 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
465
466 SkScalar xRadius = SkScalarHalf(xformedRect.width());
467 SkScalar yRadius = SkScalarHalf(xformedRect.height());
468 SkScalar innerXRadius = 0.0f;
469 SkScalar innerRatio = 1.0f;
470
471 if (SkStrokeRec::kFill_Style != style) {
472 SkScalar strokeWidth = stroke.getWidth();
473
474 // do (potentially) anisotropic mapping
475 SkVector scaledStroke;
476 scaledStroke.set(strokeWidth, strokeWidth);
477 vm.mapVectors(&scaledStroke, 1);
478
479 if (SkScalarNearlyZero(scaledStroke.length())) {
480 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
481 } else {
482 scaledStroke.scale(0.5f);
483 }
484
485 // this is legit only if scale & translation (which should be the case at the moment)
486 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
487 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
488 if (innerYRadius > SK_ScalarNearlyZero) {
489 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
490 innerRatio = innerXRadius/innerYRadius;
491 }
492 }
493 xRadius += scaledStroke.fX;
494 yRadius += scaledStroke.fY;
495 }
496
497 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
498
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000499 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000500 // This will also expand the rect so all the pixels will be captured.
501 xRadius += SK_ScalarHalf;
502 yRadius += SK_ScalarHalf;
503 innerXRadius -= SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000504
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000505 SkRect bounds = SkRect::MakeLTRB(
506 center.fX - xRadius,
507 center.fY - yRadius,
508 center.fX + xRadius,
509 center.fY + yRadius
510 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000511
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000512 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up
513 // with a circle equation which can be checked quickly in the shader. We need one offset for
514 // outer and one for inner because they have different scale factors -- otherwise we end up with
515 // non-uniform strokes.
516 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
517 verts[0].fOuterXRadius = xRadius;
518 verts[0].fInnerXRadius = innerXRadius;
519 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius);
520 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius);
521
522 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
523 verts[1].fOuterXRadius = xRadius;
524 verts[1].fInnerXRadius = innerXRadius;
525 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
526 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
527
528 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
529 verts[2].fOuterXRadius = xRadius;
530 verts[2].fInnerXRadius = innerXRadius;
531 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius);
532 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius);
533
534 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
535 verts[3].fOuterXRadius = xRadius;
536 verts[3].fInnerXRadius = innerXRadius;
537 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius);
538 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius);
539
540 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000541}