GrOvalRenderer.cpp revision 6fc1b4998917791a73bf54428513940fe77dc058
1/*
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
10#include "GrEffect.h"
11#include "gl/GrGLEffect.h"
12#include "gl/GrGLSL.h"
13#include "GrTBackendEffectFactory.h"
14
15#include "GrDrawState.h"
16#include "GrDrawTarget.h"
17#include "GrGpu.h"
18
19#include "SkRRect.h"
20#include "SkStrokeRec.h"
21
22SK_DEFINE_INST_COUNT(GrOvalRenderer)
23
24namespace {
25
26struct CircleVertex {
27    GrPoint  fPos;
28    GrPoint  fOffset;
29    SkScalar fOuterRadius;
30    SkScalar fInnerRadius;
31};
32
33struct EllipseVertex {
34    GrPoint  fPos;
35    GrPoint  fOffset;
36    GrPoint  fOuterRadii;
37    GrPoint  fInnerRadii;
38};
39
40struct DIEllipseVertex {
41    GrPoint  fPos;
42    GrPoint  fOuterOffset;
43    GrPoint  fInnerOffset;
44};
45
46inline bool circle_stays_circle(const SkMatrix& m) {
47    return m.isSimilarity();
48}
49
50}
51
52///////////////////////////////////////////////////////////////////////////////
53
54/**
55 * The output of this effect is a modulation of the input color and coverage for a circle,
56 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
57 */
58
59class CircleEdgeEffect : public GrEffect {
60public:
61    static GrEffectRef* Create(bool stroke) {
62        GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
63        GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
64
65        if (stroke) {
66            gCircleStrokeEdge->ref();
67            return gCircleStrokeEdge;
68        } else {
69            gCircleFillEdge->ref();
70            return gCircleFillEdge;
71        }
72    }
73
74    virtual void getConstantColorComponents(GrColor* color,
75                                            uint32_t* validFlags) const SK_OVERRIDE {
76        *validFlags = 0;
77    }
78
79    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
80        return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
81    }
82
83    virtual ~CircleEdgeEffect() {}
84
85    static const char* Name() { return "CircleEdge"; }
86
87    inline bool isStroked() const { return fStroke; }
88
89    class GLEffect : public GrGLEffect {
90    public:
91        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
92        : INHERITED (factory) {}
93
94        virtual void emitCode(GrGLShaderBuilder* builder,
95                              const GrDrawEffect& drawEffect,
96                              EffectKey key,
97                              const char* outputColor,
98                              const char* inputColor,
99                              const TextureSamplerArray& samplers) SK_OVERRIDE {
100            GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
101            SkASSERT(NULL != vertexBuilder);
102
103            const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
104            const char *vsName, *fsName;
105            vertexBuilder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
106
107            const SkString* attrName =
108                vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
109            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
110
111            builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
112            builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
113            if (circleEffect.isStroked()) {
114                builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
115                builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
116            }
117
118            SkString modulate;
119            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
120            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
121        }
122
123        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
124            const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
125
126            return circleEffect.isStroked() ? 0x1 : 0x0;
127        }
128
129        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
130
131    private:
132        typedef GrGLEffect INHERITED;
133    };
134
135
136private:
137    CircleEdgeEffect(bool stroke) : GrEffect() {
138        this->addVertexAttrib(kVec4f_GrSLType);
139        fStroke = stroke;
140    }
141
142    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
143        const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
144        return cee.fStroke == fStroke;
145    }
146
147    bool fStroke;
148
149    GR_DECLARE_EFFECT_TEST;
150
151    typedef GrEffect INHERITED;
152};
153
154GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
155
156GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
157                                          GrContext* context,
158                                          const GrDrawTargetCaps&,
159                                          GrTexture* textures[]) {
160    return CircleEdgeEffect::Create(random->nextBool());
161}
162
163///////////////////////////////////////////////////////////////////////////////
164
165/**
166 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
167 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
168 * in both x and y directions.
169 *
170 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
171 */
172
173class EllipseEdgeEffect : public GrEffect {
174public:
175    static GrEffectRef* Create(bool stroke) {
176        GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
177        GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
178
179        if (stroke) {
180            gEllipseStrokeEdge->ref();
181            return gEllipseStrokeEdge;
182        } else {
183            gEllipseFillEdge->ref();
184            return gEllipseFillEdge;
185        }
186    }
187
188    virtual void getConstantColorComponents(GrColor* color,
189                                            uint32_t* validFlags) const SK_OVERRIDE {
190        *validFlags = 0;
191    }
192
193    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
194        return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
195    }
196
197    virtual ~EllipseEdgeEffect() {}
198
199    static const char* Name() { return "EllipseEdge"; }
200
201    inline bool isStroked() const { return fStroke; }
202
203    class GLEffect : public GrGLEffect {
204    public:
205        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
206        : INHERITED (factory) {}
207
208        virtual void emitCode(GrGLShaderBuilder* builder,
209                              const GrDrawEffect& drawEffect,
210                              EffectKey key,
211                              const char* outputColor,
212                              const char* inputColor,
213                              const TextureSamplerArray& samplers) SK_OVERRIDE {
214            GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
215            SkASSERT(NULL != vertexBuilder);
216
217            const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
218
219            const char *vsOffsetName, *fsOffsetName;
220            const char *vsRadiiName, *fsRadiiName;
221
222            vertexBuilder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
223            const SkString* attr0Name =
224                vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
225            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
226
227            vertexBuilder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
228            const SkString* attr1Name =
229                vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
230            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
231
232            // for outer curve
233            builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName);
234            builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
235            builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
236            builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
237            // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
238            // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
239            // TODO: restrict this to Adreno-only
240            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
241            builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
242            builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
243
244            // for inner curve
245            if (ellipseEffect.isStroked()) {
246                builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName);
247                builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
248                builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName);
249                builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
250                builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
251            }
252
253            SkString modulate;
254            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
255            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
256        }
257
258        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
259            const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
260
261            return ellipseEffect.isStroked() ? 0x1 : 0x0;
262        }
263
264        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
265        }
266
267    private:
268        typedef GrGLEffect INHERITED;
269    };
270
271private:
272    EllipseEdgeEffect(bool stroke) : GrEffect() {
273        this->addVertexAttrib(kVec2f_GrSLType);
274        this->addVertexAttrib(kVec4f_GrSLType);
275        fStroke = stroke;
276    }
277
278    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
279        const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
280        return eee.fStroke == fStroke;
281    }
282
283    bool fStroke;
284
285    GR_DECLARE_EFFECT_TEST;
286
287    typedef GrEffect INHERITED;
288};
289
290GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
291
292GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
293                                           GrContext* context,
294                                           const GrDrawTargetCaps&,
295                                           GrTexture* textures[]) {
296    return EllipseEdgeEffect::Create(random->nextBool());
297}
298
299///////////////////////////////////////////////////////////////////////////////
300
301/**
302 * The output of this effect is a modulation of the input color and coverage for an ellipse,
303 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
304 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
305 * using differentials.
306 *
307 * The result is device-independent and can be used with any affine matrix.
308 */
309
310class DIEllipseEdgeEffect : public GrEffect {
311public:
312    enum Mode { kStroke = 0, kHairline, kFill };
313
314    static GrEffectRef* Create(Mode mode) {
315        GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke));
316        GR_CREATE_STATIC_EFFECT(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline));
317        GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill));
318
319        if (kStroke == mode) {
320            gEllipseStrokeEdge->ref();
321            return gEllipseStrokeEdge;
322        } else if (kHairline == mode) {
323            gEllipseHairlineEdge->ref();
324            return gEllipseHairlineEdge;
325        } else {
326            gEllipseFillEdge->ref();
327            return gEllipseFillEdge;
328        }
329    }
330
331    virtual void getConstantColorComponents(GrColor* color,
332                                            uint32_t* validFlags) const SK_OVERRIDE {
333        *validFlags = 0;
334    }
335
336    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
337        return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance();
338    }
339
340    virtual ~DIEllipseEdgeEffect() {}
341
342    static const char* Name() { return "DIEllipseEdge"; }
343
344    inline Mode getMode() const { return fMode; }
345
346    class GLEffect : public GrGLEffect {
347    public:
348        GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
349        : INHERITED (factory) {}
350
351        virtual void emitCode(GrGLShaderBuilder* builder,
352                              const GrDrawEffect& drawEffect,
353                              EffectKey key,
354                              const char* outputColor,
355                              const char* inputColor,
356                              const TextureSamplerArray& samplers) SK_OVERRIDE {
357            GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
358            SkASSERT(NULL != vertexBuilder);
359
360            const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
361
362            SkAssertResult(builder->enableFeature(
363                                              GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
364
365            const char *vsOffsetName, *fsOffsetName;
366            vertexBuilder->addVarying(kVec4f_GrSLType, "EllipseOffsets",
367                                      &vsOffsetName, &fsOffsetName);
368            const SkString* attr0Name =
369                vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
370            vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
371
372            // for outer curve
373            builder->fsCodeAppendf("\tvec2 scaledOffset = %s.xy;\n", fsOffsetName);
374            builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
375            builder->fsCodeAppendf("\tvec4 duvdx = dFdx(%s);\n", fsOffsetName);
376            builder->fsCodeAppendf("\tvec4 duvdy = dFdy(%s);\n", fsOffsetName);
377            builder->fsCodeAppendf("\tvec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
378                                   "\t                 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
379                                   fsOffsetName, fsOffsetName, fsOffsetName, fsOffsetName);
380
381            builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
382            // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
383            // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
384            // TODO: restrict this to Adreno-only
385            builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
386            builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
387            if (kHairline == ellipseEffect.getMode()) {
388                // can probably do this with one step
389                builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n");
390                builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n");
391            } else {
392                builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
393            }
394
395            // for inner curve
396            if (kStroke == ellipseEffect.getMode()) {
397                builder->fsCodeAppendf("\tscaledOffset = %s.zw;\n", fsOffsetName);
398                builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
399                builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.z*duvdx.z + 2.0*%s.w*duvdx.w,\n"
400                                       "\t            2.0*%s.z*duvdy.z + 2.0*%s.w*duvdy.w);\n",
401                                       fsOffsetName, fsOffsetName, fsOffsetName, fsOffsetName);
402                builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
403                builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
404            }
405
406            SkString modulate;
407            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
408            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
409        }
410
411        static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
412            const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
413
414            return ellipseEffect.getMode();
415        }
416
417        virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
418        }
419
420    private:
421        typedef GrGLEffect INHERITED;
422    };
423
424private:
425    DIEllipseEdgeEffect(Mode mode) : GrEffect() {
426        this->addVertexAttrib(kVec4f_GrSLType);
427        fMode = mode;
428    }
429
430    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
431        const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other);
432        return eee.fMode == fMode;
433    }
434
435    Mode fMode;
436
437    GR_DECLARE_EFFECT_TEST;
438
439    typedef GrEffect INHERITED;
440};
441
442GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect);
443
444GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkMWCRandom* random,
445                                             GrContext* context,
446                                             const GrDrawTargetCaps&,
447                                             GrTexture* textures[]) {
448    return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2)));
449}
450
451///////////////////////////////////////////////////////////////////////////////
452
453void GrOvalRenderer::reset() {
454    GrSafeSetNull(fRRectIndexBuffer);
455}
456
457bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
458                              const SkRect& oval, const SkStrokeRec& stroke)
459{
460    if (!useAA) {
461        return false;
462    }
463
464    const SkMatrix& vm = context->getMatrix();
465
466    // we can draw circles
467    if (SkScalarNearlyEqual(oval.width(), oval.height())
468        && circle_stays_circle(vm)) {
469        this->drawCircle(target, useAA, oval, stroke);
470    // if we have shader derivative support, render as device-independent
471    } else if (target->caps()->shaderDerivativeSupport()) {
472        return this->drawDIEllipse(target, useAA, oval, stroke);
473    // otherwise axis-aligned ellipses only
474    } else if (vm.rectStaysRect()) {
475        return this->drawEllipse(target, useAA, oval, stroke);
476    } else {
477        return false;
478    }
479
480    return true;
481}
482
483///////////////////////////////////////////////////////////////////////////////
484
485// position + edge
486extern const GrVertexAttrib gCircleVertexAttribs[] = {
487    {kVec2f_GrVertexAttribType, 0,               kPosition_GrVertexAttribBinding},
488    {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
489};
490
491void GrOvalRenderer::drawCircle(GrDrawTarget* target,
492                                bool useAA,
493                                const SkRect& circle,
494                                const SkStrokeRec& stroke)
495{
496    GrDrawState* drawState = target->drawState();
497
498    const SkMatrix& vm = drawState->getViewMatrix();
499    GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
500    vm.mapPoints(&center, 1);
501    SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
502    SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
503
504    GrDrawState::AutoViewMatrixRestore avmr;
505    if (!avmr.setIdentity(drawState)) {
506        return;
507    }
508
509    drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
510    SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
511
512    GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
513    if (!geo.succeeded()) {
514        GrPrintf("Failed to get space for vertices!\n");
515        return;
516    }
517
518    CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
519
520    SkStrokeRec::Style style = stroke.getStyle();
521    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
522
523    SkScalar innerRadius = 0.0f;
524    SkScalar outerRadius = radius;
525    SkScalar halfWidth = 0;
526    if (style != SkStrokeRec::kFill_Style) {
527        if (SkScalarNearlyZero(strokeWidth)) {
528            halfWidth = SK_ScalarHalf;
529        } else {
530            halfWidth = SkScalarHalf(strokeWidth);
531        }
532
533        outerRadius += halfWidth;
534        if (isStroked) {
535            innerRadius = radius - halfWidth;
536        }
537    }
538
539    GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
540    static const int kCircleEdgeAttrIndex = 1;
541    drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
542
543    // The radii are outset for two reasons. First, it allows the shader to simply perform
544    // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
545    // verts of the bounding box that is rendered and the outset ensures the box will cover all
546    // pixels partially covered by the circle.
547    outerRadius += SK_ScalarHalf;
548    innerRadius -= SK_ScalarHalf;
549
550    SkRect bounds = SkRect::MakeLTRB(
551        center.fX - outerRadius,
552        center.fY - outerRadius,
553        center.fX + outerRadius,
554        center.fY + outerRadius
555    );
556
557    verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
558    verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
559    verts[0].fOuterRadius = outerRadius;
560    verts[0].fInnerRadius = innerRadius;
561
562    verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
563    verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
564    verts[1].fOuterRadius = outerRadius;
565    verts[1].fInnerRadius = innerRadius;
566
567    verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
568    verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
569    verts[2].fOuterRadius = outerRadius;
570    verts[2].fInnerRadius = innerRadius;
571
572    verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
573    verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
574    verts[3].fOuterRadius = outerRadius;
575    verts[3].fInnerRadius = innerRadius;
576
577    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
578}
579
580///////////////////////////////////////////////////////////////////////////////
581
582// position + offset + 1/radii
583extern const GrVertexAttrib gEllipseVertexAttribs[] = {
584    {kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding},
585    {kVec2f_GrVertexAttribType, sizeof(GrPoint),   kEffect_GrVertexAttribBinding},
586    {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
587};
588
589// position + offsets
590extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
591    {kVec2f_GrVertexAttribType, 0,                 kPosition_GrVertexAttribBinding},
592    {kVec4f_GrVertexAttribType, sizeof(GrPoint),   kEffect_GrVertexAttribBinding},
593};
594
595bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
596                                 bool useAA,
597                                 const SkRect& ellipse,
598                                 const SkStrokeRec& stroke)
599{
600    GrDrawState* drawState = target->drawState();
601#ifdef SK_DEBUG
602    {
603        // we should have checked for this previously
604        bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
605        SkASSERT(useAA && isAxisAlignedEllipse);
606    }
607#endif
608
609    // do any matrix crunching before we reset the draw state for device coords
610    const SkMatrix& vm = drawState->getViewMatrix();
611    GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
612    vm.mapPoints(&center, 1);
613    SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
614    SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
615    SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
616                                   vm[SkMatrix::kMSkewY]*ellipseYRadius);
617    SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
618                                   vm[SkMatrix::kMScaleY]*ellipseYRadius);
619
620    // do (potentially) anisotropic mapping of stroke
621    SkVector scaledStroke;
622    SkScalar strokeWidth = stroke.getWidth();
623    scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
624    scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
625
626    SkStrokeRec::Style style = stroke.getStyle();
627    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
628
629    SkScalar innerXRadius = 0;
630    SkScalar innerYRadius = 0;
631    if (SkStrokeRec::kFill_Style != style) {
632        if (SkScalarNearlyZero(scaledStroke.length())) {
633            scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
634        } else {
635            scaledStroke.scale(SK_ScalarHalf);
636        }
637
638        // we only handle thick strokes for near-circular ellipses
639        if (scaledStroke.length() > SK_ScalarHalf &&
640            (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
641            return false;
642        }
643
644        // we don't handle it if curvature of the stroke is less than curvature of the ellipse
645        if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
646            scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
647            return false;
648        }
649
650        // this is legit only if scale & translation (which should be the case at the moment)
651        if (isStroked) {
652            innerXRadius = xRadius - scaledStroke.fX;
653            innerYRadius = yRadius - scaledStroke.fY;
654        }
655
656        xRadius += scaledStroke.fX;
657        yRadius += scaledStroke.fY;
658    }
659
660    GrDrawState::AutoViewMatrixRestore avmr;
661    if (!avmr.setIdentity(drawState)) {
662        return false;
663    }
664
665    drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
666    SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
667
668    GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
669    if (!geo.succeeded()) {
670        GrPrintf("Failed to get space for vertices!\n");
671        return false;
672    }
673
674    EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
675
676    GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
677                                                    innerXRadius > 0 && innerYRadius > 0);
678
679    static const int kEllipseCenterAttrIndex = 1;
680    drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex)->unref();
681
682    // Compute the reciprocals of the radii here to save time in the shader
683    SkScalar xRadRecip = SkScalarInvert(xRadius);
684    SkScalar yRadRecip = SkScalarInvert(yRadius);
685    SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
686    SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
687
688    // We've extended the outer x radius out half a pixel to antialias.
689    // This will also expand the rect so all the pixels will be captured.
690    // TODO: Consider if we should use sqrt(2)/2 instead
691    xRadius += SK_ScalarHalf;
692    yRadius += SK_ScalarHalf;
693
694    SkRect bounds = SkRect::MakeLTRB(
695        center.fX - xRadius,
696        center.fY - yRadius,
697        center.fX + xRadius,
698        center.fY + yRadius
699    );
700
701    verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
702    verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
703    verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
704    verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
705
706    verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
707    verts[1].fOffset = SkPoint::Make(xRadius, -yRadius);
708    verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
709    verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
710
711    verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
712    verts[2].fOffset = SkPoint::Make(-xRadius, yRadius);
713    verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
714    verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
715
716    verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
717    verts[3].fOffset = SkPoint::Make(xRadius, yRadius);
718    verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
719    verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
720
721    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
722
723    return true;
724}
725
726bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
727                                   bool useAA,
728                                   const SkRect& ellipse,
729                                   const SkStrokeRec& stroke)
730{
731    GrDrawState* drawState = target->drawState();
732    const SkMatrix& vm = drawState->getViewMatrix();
733
734    GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
735    SkScalar xRadius = SkScalarHalf(ellipse.width());
736    SkScalar yRadius = SkScalarHalf(ellipse.height());
737
738    SkStrokeRec::Style style = stroke.getStyle();
739    DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
740                                    DIEllipseEdgeEffect::kStroke :
741                                    (SkStrokeRec::kHairline_Style == style) ?
742                                    DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
743
744    SkScalar innerXRadius = 0;
745    SkScalar innerYRadius = 0;
746    if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
747        SkScalar strokeWidth = stroke.getWidth();
748
749        if (SkScalarNearlyZero(strokeWidth)) {
750            strokeWidth = SK_ScalarHalf;
751        } else {
752            strokeWidth *= SK_ScalarHalf;
753        }
754
755        // we only handle thick strokes for near-circular ellipses
756        if (strokeWidth > SK_ScalarHalf &&
757            (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
758            return false;
759        }
760
761        // we don't handle it if curvature of the stroke is less than curvature of the ellipse
762        if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
763            strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
764            return false;
765        }
766
767        // set inner radius (if needed)
768        if (SkStrokeRec::kStroke_Style == style) {
769            innerXRadius = xRadius - strokeWidth;
770            innerYRadius = yRadius - strokeWidth;
771        }
772
773        xRadius += strokeWidth;
774        yRadius += strokeWidth;
775    }
776    if (DIEllipseEdgeEffect::kStroke == mode) {
777        mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
778                                                        DIEllipseEdgeEffect::kFill;
779    }
780    SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
781    SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
782
783    drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs));
784    SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize());
785
786    GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
787    if (!geo.succeeded()) {
788        GrPrintf("Failed to get space for vertices!\n");
789        return false;
790    }
791
792    DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
793
794    GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode);
795
796    static const int kEllipseOuterOffsetAttrIndex = 1;
797    static const int kEllipseInnerOffsetAttrIndex = 2;
798    drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex,
799                                         kEllipseInnerOffsetAttrIndex)->unref();
800
801    // This expands the outer rect so that after CTM we end up with a half-pixel border
802    SkScalar a = vm[SkMatrix::kMScaleX];
803    SkScalar b = vm[SkMatrix::kMSkewX];
804    SkScalar c = vm[SkMatrix::kMSkewY];
805    SkScalar d = vm[SkMatrix::kMScaleY];
806    SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
807    SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
808    // This adjusts the "radius" to include the half-pixel border
809    SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
810    SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
811
812    SkRect bounds = SkRect::MakeLTRB(
813        center.fX - xRadius - geoDx,
814        center.fY - yRadius - geoDy,
815        center.fX + xRadius + geoDx,
816        center.fY + yRadius + geoDy
817    );
818
819    verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
820    verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
821    verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
822
823    verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
824    verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
825    verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
826
827    verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
828    verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
829    verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
830
831    verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
832    verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
833    verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
834
835    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
836
837    return true;
838}
839
840///////////////////////////////////////////////////////////////////////////////
841
842static const uint16_t gRRectIndices[] = {
843    // corners
844    0, 1, 5, 0, 5, 4,
845    2, 3, 7, 2, 7, 6,
846    8, 9, 13, 8, 13, 12,
847    10, 11, 15, 10, 15, 14,
848
849    // edges
850    1, 2, 6, 1, 6, 5,
851    4, 5, 9, 4, 9, 8,
852    6, 7, 11, 6, 11, 10,
853    9, 10, 14, 9, 14, 13,
854
855    // center
856    // we place this at the end so that we can ignore these indices when rendering stroke-only
857    5, 6, 10, 5, 10, 9
858};
859
860
861GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
862    if (NULL == fRRectIndexBuffer) {
863        fRRectIndexBuffer =
864        gpu->createIndexBuffer(sizeof(gRRectIndices), false);
865        if (NULL != fRRectIndexBuffer) {
866#ifdef SK_DEBUG
867            bool updated =
868#endif
869            fRRectIndexBuffer->updateData(gRRectIndices,
870                                          sizeof(gRRectIndices));
871            GR_DEBUGASSERT(updated);
872        }
873    }
874    return fRRectIndexBuffer;
875}
876
877bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
878                                     const SkRRect& rrect, const SkStrokeRec& stroke)
879{
880    // only anti-aliased rrects for now
881    if (!useAA) {
882        return false;
883    }
884
885    const SkMatrix& vm = context->getMatrix();
886#ifdef SK_DEBUG
887    {
888        // we should have checked for this previously
889        SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple());
890    }
891#endif
892
893    // do any matrix crunching before we reset the draw state for device coords
894    const SkRect& rrectBounds = rrect.getBounds();
895    SkRect bounds;
896    vm.mapRect(&bounds, rrectBounds);
897
898    SkVector radii = rrect.getSimpleRadii();
899    SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
900                                   vm[SkMatrix::kMSkewY]*radii.fY);
901    SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
902                                   vm[SkMatrix::kMScaleY]*radii.fY);
903
904    // if hairline stroke is greater than radius, we don't handle that right now
905    SkStrokeRec::Style style = stroke.getStyle();
906    if (SkStrokeRec::kHairline_Style == style &&
907        (SK_ScalarHalf >= xRadius || SK_ScalarHalf >= yRadius)) {
908        return false;
909    }
910
911    // do (potentially) anisotropic mapping of stroke
912    SkVector scaledStroke;
913    SkScalar strokeWidth = stroke.getWidth();
914    scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
915    scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
916
917    // if half of strokewidth is greater than radius, we don't handle that right now
918    if (SK_ScalarHalf*scaledStroke.fX >= xRadius || SK_ScalarHalf*scaledStroke.fY >= yRadius) {
919        return false;
920    }
921
922    // reset to device coordinates
923    GrDrawState* drawState = target->drawState();
924    GrDrawState::AutoViewMatrixRestore avmr;
925    if (!avmr.setIdentity(drawState)) {
926        return false;
927    }
928
929    bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
930
931    GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
932    if (NULL == indexBuffer) {
933        GrPrintf("Failed to create index buffer!\n");
934        return false;
935    }
936
937    // if the corners are circles, use the circle renderer
938    if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
939        drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
940        SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
941
942        GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
943        if (!geo.succeeded()) {
944            GrPrintf("Failed to get space for vertices!\n");
945            return false;
946        }
947        CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
948
949        SkScalar innerRadius = 0.0f;
950        SkScalar outerRadius = xRadius;
951        SkScalar halfWidth = 0;
952        if (style != SkStrokeRec::kFill_Style) {
953            if (SkScalarNearlyZero(scaledStroke.fX)) {
954                halfWidth = SK_ScalarHalf;
955            } else {
956                halfWidth = SkScalarHalf(scaledStroke.fX);
957            }
958
959            if (isStroked) {
960                innerRadius = xRadius - halfWidth;
961            }
962            outerRadius += halfWidth;
963            bounds.outset(halfWidth, halfWidth);
964        }
965
966        isStroked = (isStroked && innerRadius > 0);
967
968        GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
969        static const int kCircleEdgeAttrIndex = 1;
970        drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
971
972        // The radii are outset for two reasons. First, it allows the shader to simply perform
973        // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
974        // verts of the bounding box that is rendered and the outset ensures the box will cover all
975        // pixels partially covered by the circle.
976        outerRadius += SK_ScalarHalf;
977        innerRadius -= SK_ScalarHalf;
978
979        // Expand the rect so all the pixels will be captured.
980        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
981
982        SkScalar yCoords[4] = {
983            bounds.fTop,
984            bounds.fTop + outerRadius,
985            bounds.fBottom - outerRadius,
986            bounds.fBottom
987        };
988        SkScalar yOuterRadii[4] = {
989            -outerRadius,
990            0,
991            0,
992            outerRadius
993        };
994        for (int i = 0; i < 4; ++i) {
995            verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
996            verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
997            verts->fOuterRadius = outerRadius;
998            verts->fInnerRadius = innerRadius;
999            verts++;
1000
1001            verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1002            verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1003            verts->fOuterRadius = outerRadius;
1004            verts->fInnerRadius = innerRadius;
1005            verts++;
1006
1007            verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1008            verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1009            verts->fOuterRadius = outerRadius;
1010            verts->fInnerRadius = innerRadius;
1011            verts++;
1012
1013            verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1014            verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
1015            verts->fOuterRadius = outerRadius;
1016            verts->fInnerRadius = innerRadius;
1017            verts++;
1018        }
1019
1020        // drop out the middle quad if we're stroked
1021        int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1022        target->setIndexSourceToBuffer(indexBuffer);
1023        target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1024
1025    // otherwise we use the ellipse renderer
1026    } else {
1027        drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
1028        SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
1029
1030        SkScalar innerXRadius = 0.0f;
1031        SkScalar innerYRadius = 0.0f;
1032        if (SkStrokeRec::kFill_Style != style) {
1033            if (SkScalarNearlyZero(scaledStroke.length())) {
1034                scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1035            } else {
1036                scaledStroke.scale(SK_ScalarHalf);
1037            }
1038
1039            // we only handle thick strokes for near-circular ellipses
1040            if (scaledStroke.length() > SK_ScalarHalf &&
1041                (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1042                return false;
1043            }
1044
1045            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1046            if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1047                scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1048                return false;
1049            }
1050
1051            // this is legit only if scale & translation (which should be the case at the moment)
1052            if (isStroked) {
1053                innerXRadius = xRadius - scaledStroke.fX;
1054                innerYRadius = yRadius - scaledStroke.fY;
1055            }
1056
1057            xRadius += scaledStroke.fX;
1058            yRadius += scaledStroke.fY;
1059            bounds.outset(scaledStroke.fX, scaledStroke.fY);
1060        }
1061
1062        isStroked = (isStroked && innerXRadius > 0 && innerYRadius > 0);
1063
1064        GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1065        if (!geo.succeeded()) {
1066            GrPrintf("Failed to get space for vertices!\n");
1067            return false;
1068        }
1069        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
1070
1071        GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
1072        static const int kEllipseOffsetAttrIndex = 1;
1073        static const int kEllipseRadiiAttrIndex = 2;
1074        drawState->addCoverageEffect(effect,
1075                                     kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
1076
1077        // Compute the reciprocals of the radii here to save time in the shader
1078        SkScalar xRadRecip = SkScalarInvert(xRadius);
1079        SkScalar yRadRecip = SkScalarInvert(yRadius);
1080        SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1081        SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
1082
1083        // Extend the radii out half a pixel to antialias.
1084        SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1085        SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
1086
1087        // Expand the rect so all the pixels will be captured.
1088        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1089
1090        SkScalar yCoords[4] = {
1091            bounds.fTop,
1092            bounds.fTop + yOuterRadius,
1093            bounds.fBottom - yOuterRadius,
1094            bounds.fBottom
1095        };
1096        SkScalar yOuterOffsets[4] = {
1097            yOuterRadius,
1098            SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1099            SK_ScalarNearlyZero,
1100            yOuterRadius
1101        };
1102
1103        for (int i = 0; i < 4; ++i) {
1104            verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1105            verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1106            verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1107            verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1108            verts++;
1109
1110            verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1111            verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1112            verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1113            verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1114            verts++;
1115
1116            verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1117            verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1118            verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1119            verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1120            verts++;
1121
1122            verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1123            verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1124            verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1125            verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1126            verts++;
1127        }
1128
1129        // drop out the middle quad if we're stroked
1130        int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1131        target->setIndexSourceToBuffer(indexBuffer);
1132        target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1133    }
1134
1135    return true;
1136}
1137