1/*
2 * Copyright 2012 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 "gm.h"
9#if SK_SUPPORT_GPU
10#include "GrTest.h"
11#include "effects/GrRRectEffect.h"
12#endif
13#include "SkDevice.h"
14#include "SkRRect.h"
15
16namespace skiagm {
17
18///////////////////////////////////////////////////////////////////////////////
19
20class RRectGM : public GM {
21public:
22    enum Type {
23        kBW_Draw_Type,
24        kAA_Draw_Type,
25        kBW_Clip_Type,
26        kAA_Clip_Type,
27        kEffect_Type,
28    };
29    RRectGM(Type type) : fType(type) {
30        this->setBGColor(0xFFDDDDDD);
31        this->setUpRRects();
32    }
33
34protected:
35    SkString onShortName() SK_OVERRIDE {
36        SkString name("rrect");
37        switch (fType) {
38            case kBW_Draw_Type:
39                name.append("_draw_bw");
40                break;
41            case kAA_Draw_Type:
42                name.append("_draw_aa");
43                break;
44            case kBW_Clip_Type:
45                name.append("_clip_bw");
46                break;
47            case kAA_Clip_Type:
48                name.append("_clip_aa");
49                break;
50            case kEffect_Type:
51                name.append("_effect");
52                break;
53        }
54        return name;
55    }
56
57    virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(kImageWidth, kImageHeight); }
58
59    virtual uint32_t onGetFlags() const SK_OVERRIDE {
60        if (kEffect_Type == fType) {
61            return kGPUOnly_Flag | kSkipTiled_Flag;
62        } else {
63            return kSkipTiled_Flag;
64        }
65    }
66
67    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
68#if SK_SUPPORT_GPU
69        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
70        GrContext* context = rt ? rt->getContext() : NULL;
71        if (kEffect_Type == fType && NULL == context) {
72            return;
73        }
74#endif
75
76        SkPaint paint;
77        if (kAA_Draw_Type == fType) {
78            paint.setAntiAlias(true);
79        }
80
81        static const SkRect kMaxTileBound = SkRect::MakeWH(SkIntToScalar(kTileX),
82                                                           SkIntToScalar(kTileY));
83#ifdef SK_DEBUG
84        static const SkRect kMaxImageBound = SkRect::MakeWH(SkIntToScalar(kImageWidth),
85                                                            SkIntToScalar(kImageHeight));
86#endif
87
88#if SK_SUPPORT_GPU
89        int lastEdgeType = (kEffect_Type == fType) ? kLast_GrProcessorEdgeType: 0;
90#else
91        int lastEdgeType = 0;
92#endif
93
94        int y = 1;
95        for (int et = 0; et <= lastEdgeType; ++et) {
96            int x = 1;
97            for (int curRRect = 0; curRRect < kNumRRects; ++curRRect) {
98                bool drew = true;
99#ifdef SK_DEBUG
100                SkASSERT(kMaxTileBound.contains(fRRects[curRRect].getBounds()));
101                SkRect imageSpaceBounds = fRRects[curRRect].getBounds();
102                imageSpaceBounds.offset(SkIntToScalar(x), SkIntToScalar(y));
103                SkASSERT(kMaxImageBound.contains(imageSpaceBounds));
104#endif
105                canvas->save();
106                    canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
107                    if (kEffect_Type == fType) {
108#if SK_SUPPORT_GPU
109                        GrTestTarget tt;
110                        context->getTestTarget(&tt);
111                        if (NULL == tt.target()) {
112                            SkDEBUGFAIL("Couldn't get Gr test target.");
113                            return;
114                        }
115                        GrDrawState* drawState = tt.target()->drawState();
116
117                        SkRRect rrect = fRRects[curRRect];
118                        rrect.offset(SkIntToScalar(x), SkIntToScalar(y));
119                        GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et;
120                        SkAutoTUnref<GrFragmentProcessor> fp(GrRRectEffect::Create(edgeType,
121                                                                                   rrect));
122                        if (fp) {
123                            drawState->addCoverageProcessor(fp);
124                            drawState->setIdentityViewMatrix();
125                            drawState->setRenderTarget(rt);
126                            drawState->setColor(0xff000000);
127
128                            SkRect bounds = rrect.getBounds();
129                            bounds.outset(2.f, 2.f);
130
131                            tt.target()->drawSimpleRect(bounds);
132                        } else {
133                            drew = false;
134                        }
135#endif
136                    } else if (kBW_Clip_Type == fType || kAA_Clip_Type == fType) {
137                        bool aaClip = (kAA_Clip_Type == fType);
138                        canvas->clipRRect(fRRects[curRRect], SkRegion::kReplace_Op, aaClip);
139                        canvas->drawRect(kMaxTileBound, paint);
140                    } else {
141                        canvas->drawRRect(fRRects[curRRect], paint);
142                    }
143                canvas->restore();
144                if (drew) {
145                    x = x + kTileX;
146                    if (x > kImageWidth) {
147                        x = 1;
148                        y += kTileY;
149                    }
150                }
151            }
152            if (x != 1) {
153                y += kTileY;
154            }
155        }
156    }
157
158    void setUpRRects() {
159        // each RRect must fit in a 0x0 -> (kTileX-2)x(kTileY-2) block. These will be tiled across
160        // the screen in kTileX x kTileY tiles. The extra empty pixels on each side are for AA.
161
162        // simple cases
163        fRRects[0].setRect(SkRect::MakeWH(kTileX-2, kTileY-2));
164        fRRects[1].setOval(SkRect::MakeWH(kTileX-2, kTileY-2));
165        fRRects[2].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 10);
166        fRRects[3].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 5);
167        // small circular corners are an interesting test case for gpu clipping
168        fRRects[4].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 1, 1);
169        fRRects[5].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.5f, 0.5f);
170        fRRects[6].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 0.2f, 0.2f);
171
172        // The first complex case needs special handling since it is a square
173        fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]);
174        for (size_t i = 1; i < SK_ARRAY_COUNT(gRadii); ++i) {
175            fRRects[kNumSimpleCases+i].setRectRadii(SkRect::MakeWH(kTileX-2, kTileY-2), gRadii[i]);
176        }
177    }
178
179private:
180    Type fType;
181
182    static const int kImageWidth = 640;
183    static const int kImageHeight = 480;
184
185    static const int kTileX = 80;
186    static const int kTileY = 40;
187
188    static const int kNumSimpleCases = 7;
189    static const int kNumComplexCases = 35;
190    static const SkVector gRadii[kNumComplexCases][4];
191
192    static const int kNumRRects = kNumSimpleCases + kNumComplexCases;
193    SkRRect fRRects[kNumRRects];
194
195    typedef GM INHERITED;
196};
197
198// Radii for the various test cases. Order is UL, UR, LR, LL
199const SkVector RRectGM::gRadii[kNumComplexCases][4] = {
200    // a circle
201    { { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY } },
202
203    // odd ball cases
204    { { 8, 8 }, { 32, 32 }, { 8, 8 }, { 32, 32 } },
205    { { 16, 8 }, { 8, 16 }, { 16, 8 }, { 8, 16 } },
206    { { 0, 0 }, { 16, 16 }, { 8, 8 }, { 32, 32 } },
207
208    // UL
209    { { 30, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
210    { { 30, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
211    { { 15, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
212
213    // UR
214    { { 0, 0 }, { 30, 30 }, { 0, 0 }, { 0, 0 } },
215    { { 0, 0 }, { 30, 15 }, { 0, 0 }, { 0, 0 } },
216    { { 0, 0 }, { 15, 30 }, { 0, 0 }, { 0, 0 } },
217
218    // LR
219    { { 0, 0 }, { 0, 0 }, { 30, 30 }, { 0, 0 } },
220    { { 0, 0 }, { 0, 0 }, { 30, 15 }, { 0, 0 } },
221    { { 0, 0 }, { 0, 0 }, { 15, 30 }, { 0, 0 } },
222
223    // LL
224    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 30 } },
225    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 15 } },
226    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 30 } },
227
228    // over-sized radii
229    { { 0, 0 }, { 100, 400 }, { 0, 0 }, { 0, 0 } },
230    { { 0, 0 }, { 400, 400 }, { 0, 0 }, { 0, 0 } },
231    { { 400, 400 }, { 400, 400 }, { 400, 400 }, { 400, 400 } },
232
233    // circular corner tabs
234    { { 0, 0 }, { 20, 20 }, { 20, 20 }, { 0, 0 } },
235    { { 20, 20 }, { 20, 20 }, { 0, 0 }, { 0, 0 } },
236    { { 0, 0 }, { 0, 0 }, { 20, 20 }, { 20, 20 } },
237    { { 20, 20 }, { 0, 0 }, { 0, 0 }, { 20, 20 } },
238
239    // small radius circular corner tabs
240    { { 0, 0 }, { 0.2f, 0.2f }, { 0.2f, 0.2f }, { 0, 0 } },
241    { { 0.3f, 0.3f }, { 0.3f, .3f }, { 0, 0 }, { 0, 0 } },
242
243    // single circular corner cases
244    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 15 } },
245    { { 0, 0 }, { 0, 0 }, { 15, 15 }, { 0, 0 } },
246    { { 0, 0 }, { 15, 15 }, { 0, 0 }, { 0, 0 } },
247    { { 15, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
248
249    // nine patch elliptical
250    { { 5, 7 }, { 8, 7 }, { 8, 12 }, { 5, 12 } },
251    { { 0, 7 }, { 8, 7 }, { 8, 12 }, { 0, 12 } },
252
253    // nine patch elliptical, small radii
254    { { 0.4f, 7 }, { 8, 7 }, { 8, 12 }, { 0.4f, 12 } },
255    { { 0.4f, 0.4f }, { 8, 0.4f }, { 8, 12 }, { 0.4f, 12 } },
256    { { 20, 0.4f }, { 18, 0.4f }, { 18, 0.4f }, { 20, 0.4f } },
257    { { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f }, { 0.3f, 0.4f } },
258
259};
260
261///////////////////////////////////////////////////////////////////////////////
262
263DEF_GM( return new RRectGM(RRectGM::kAA_Draw_Type); )
264DEF_GM( return new RRectGM(RRectGM::kBW_Draw_Type); )
265DEF_GM( return new RRectGM(RRectGM::kAA_Clip_Type); )
266DEF_GM( return new RRectGM(RRectGM::kBW_Clip_Type); )
267#if SK_SUPPORT_GPU
268DEF_GM( return new RRectGM(RRectGM::kEffect_Type); )
269#endif
270
271}
272