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