GrOvalRenderer.cpp revision 64b682ca42c75667e49251d3ab04f192f92d0dd8
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 "SkStrokeRec.h"
18
19SK_DEFINE_INST_COUNT(GrOvalRenderer)
20
21namespace {
22
23struct CircleVertex {
24    GrPoint  fPos;
25    GrPoint  fOffset;
26    SkScalar fOuterRadius;
27    SkScalar fInnerRadius;
28};
29
30struct EllipseVertex {
31    GrPoint  fPos;
32    SkScalar fOuterXRadius;
33    SkScalar fInnerXRadius;
34    GrPoint  fOuterOffset;
35    GrPoint  fInnerOffset;
36};
37
38inline bool circle_stays_circle(const SkMatrix& m) {
39    return m.isSimilarity();
40}
41
42}
43
44///////////////////////////////////////////////////////////////////////////////
45
46/**
47 * The output of this effect is a modulation of the input color and coverage for a circle,
48 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
49 */
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
69    virtual void getConstantColorComponents(GrColor* color,
70                                            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
103            builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
104            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            }
109
110            SkString modulate;
111            GrGLSLModulatef<4>(&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
159 * ellipse, specified as  outer and inner radii, and outer and inner offsets from center.
160 */
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
180    virtual void getConstantColorComponents(GrColor* color,
181                                            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
208            const char *vsRadiiName, *fsRadiiName;
209            const char *vsOffsetsName, *fsOffsetsName;
210
211            builder->addVarying(kVec2f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
212            const SkString* attr0Name =
213                builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
214            builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr0Name->c_str());
215
216            builder->addVarying(kVec4f_GrSLType, "EllipseOffsets", &vsOffsetsName, &fsOffsetsName);
217            const SkString* attr1Name =
218                builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
219            builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetsName, attr1Name->c_str());
220
221            // get length of offset
222            builder->fsCodeAppendf("\tfloat dOuter = length(%s.xy);\n", fsOffsetsName);
223            // compare outer lengths against xOuterRadius
224            builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n",
225                                   fsRadiiName);
226
227            if (ellipseEffect.isStroked()) {
228                builder->fsCodeAppendf("\tfloat dInner = length(%s.zw);\n", fsOffsetsName);
229
230                // compare inner lengths against xInnerRadius
231                builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.y, 0.0, 1.0);\n",
232                                       fsRadiiName);
233                builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
234            }
235
236            SkString modulate;
237            GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
238            builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
239        }
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
284bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
285                    const GrRect& oval, const SkStrokeRec& stroke)
286{
287    if (!paint.isAntiAlias()) {
288        return false;
289    }
290
291    const SkMatrix& vm = context->getMatrix();
292
293    // we can draw circles
294    if (SkScalarNearlyEqual(oval.width(), oval.height())
295        && circle_stays_circle(vm)) {
296        drawCircle(target, paint, oval, stroke);
297
298    // and axis-aligned ellipses only
299    } else if (vm.rectStaysRect()) {
300        return drawEllipse(target, paint, oval, stroke);
301
302    } else {
303        return false;
304    }
305
306    return true;
307}
308
309void GrOvalRenderer::drawCircle(GrDrawTarget* target,
310                                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[] = {
329        {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
330        {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
331    };
332    drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
333    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    };
351
352    GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
353    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
372    // 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
379    SkRect bounds = SkRect::MakeLTRB(
380        center.fX - outerRadius,
381        center.fY - outerRadius,
382        center.fX + outerRadius,
383        center.fY + outerRadius
384    );
385
386    verts[0].fPos = SkPoint::Make(bounds.fLeft,  bounds.fTop);
387    verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
388    verts[0].fOuterRadius = outerRadius;
389    verts[0].fInnerRadius = innerRadius;
390
391    verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
392    verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
393    verts[1].fOuterRadius = outerRadius;
394    verts[1].fInnerRadius = innerRadius;
395
396    verts[2].fPos = SkPoint::Make(bounds.fLeft,  bounds.fBottom);
397    verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
398    verts[2].fOuterRadius = outerRadius;
399    verts[2].fInnerRadius = innerRadius;
400
401    verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
402    verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
403    verts[3].fOuterRadius = outerRadius;
404    verts[3].fInnerRadius = innerRadius;
405
406    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
407}
408
409bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
410                                 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    // do any matrix crunching before we reset the draw state for device coords
424    const SkMatrix& vm = drawState->getViewMatrix();
425    GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
426    vm.mapPoints(&center, 1);
427    SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
428    SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
429    SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
430                                   vm[SkMatrix::kMSkewY]*ellipseYRadius);
431    SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
432                                   vm[SkMatrix::kMScaleY]*ellipseYRadius);
433    if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
434        return false;
435    }
436
437    // 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
443    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
444    if (!adcd.succeeded()) {
445        return false;
446    }
447
448    // position + edge
449    static const GrVertexAttrib kVertexAttribs[] = {
450        {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
451        {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
452        {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
453    };
454    drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs));
455    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");
460        return false;
461    }
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    };
473
474    GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
475    static const int kEllipseCenterAttrIndex = 1;
476    static const int kEllipseEdgeAttrIndex = 2;
477    drawState->setEffect(kEdgeEffectStage, effect,
478                         kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
479
480    SkScalar innerXRadius = 0.0f;
481    SkScalar innerRatio = 1.0f;
482
483    if (SkStrokeRec::kFill_Style != style) {
484
485
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
506    // We've extended the outer x radius out half a pixel to antialias.
507    // 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;
511
512    SkRect bounds = SkRect::MakeLTRB(
513        center.fX - xRadius,
514        center.fY - yRadius,
515        center.fX + xRadius,
516        center.fY + yRadius
517    );
518
519    // 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
522    // 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;
532    verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
533    verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
534
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);
546
547    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
548
549    return true;
550}
551