1/*
2 * Copyright 2014 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
11#include "SkBitmap.h"
12#include "SkGradientShader.h"
13#include "SkPath.h"
14#include "SkTLList.h"
15
16static SkBitmap make_bmp(int w, int h) {
17    SkBitmap bmp;
18    bmp.allocN32Pixels(w, h, true);
19
20    SkCanvas canvas(bmp);
21    SkScalar wScalar = SkIntToScalar(w);
22    SkScalar hScalar = SkIntToScalar(h);
23
24    SkPoint     pt = { wScalar / 2, hScalar / 2 };
25
26    SkScalar    radius = 3 * SkMaxScalar(wScalar, hScalar);
27
28    SkColor     colors[] = { sk_tool_utils::color_to_565(SK_ColorDKGRAY),
29                             sk_tool_utils::color_to_565(0xFF222255),
30                             sk_tool_utils::color_to_565(0xFF331133),
31                             sk_tool_utils::color_to_565(0xFF884422),
32                             sk_tool_utils::color_to_565(0xFF000022), SK_ColorWHITE,
33                             sk_tool_utils::color_to_565(0xFFAABBCC) };
34
35    SkScalar    pos[] = {0,
36                         SK_Scalar1 / 6,
37                         2 * SK_Scalar1 / 6,
38                         3 * SK_Scalar1 / 6,
39                         4 * SK_Scalar1 / 6,
40                         5 * SK_Scalar1 / 6,
41                         SK_Scalar1};
42
43    SkPaint paint;
44    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
45    SkMatrix mat = SkMatrix::I();
46    for (int i = 0; i < 4; ++i) {
47        paint.setShader(SkGradientShader::MakeRadial(
48                        pt, radius,
49                        colors, pos,
50                        SK_ARRAY_COUNT(colors),
51                        SkShader::kRepeat_TileMode,
52                        0, &mat));
53        canvas.drawRect(rect, paint);
54        rect.inset(wScalar / 8, hScalar / 8);
55        mat.preTranslate(6 * wScalar, 6 * hScalar);
56        mat.postScale(SK_Scalar1 / 3, SK_Scalar1 / 3);
57    }
58
59    paint.setAntiAlias(true);
60    sk_tool_utils::set_portable_typeface(&paint);
61    paint.setTextSize(wScalar / 2.2f);
62    paint.setShader(0);
63    paint.setColor(sk_tool_utils::color_to_565(SK_ColorLTGRAY));
64    constexpr char kTxt[] = "Skia";
65    SkPoint texPos = { wScalar / 17, hScalar / 2 + paint.getTextSize() / 2.5f };
66    canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint);
67    paint.setColor(SK_ColorBLACK);
68    paint.setStyle(SkPaint::kStroke_Style);
69    paint.setStrokeWidth(SK_Scalar1);
70    canvas.drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1, texPos.fX, texPos.fY, paint);
71    return bmp;
72}
73
74namespace skiagm {
75/**
76 * This GM tests convex polygon clips.
77 */
78class ConvexPolyClip : public GM {
79public:
80    ConvexPolyClip() {
81        this->setBGColor(0xFFFFFFFF);
82    }
83
84protected:
85    SkString onShortName() override {
86        return SkString("convex_poly_clip");
87    }
88
89    SkISize onISize() override {
90        // When benchmarking the saveLayer set of draws is skipped.
91        int w = 435;
92        if (kBench_Mode != this->getMode()) {
93            w *= 2;
94        }
95        return SkISize::Make(w, 540);
96    }
97
98    void onOnceBeforeDraw() override {
99        SkPath tri;
100        tri.moveTo(5.f, 5.f);
101        tri.lineTo(100.f, 20.f);
102        tri.lineTo(15.f, 100.f);
103
104        fClips.addToTail()->setPath(tri);
105
106        SkPath hexagon;
107        constexpr SkScalar kRadius = 45.f;
108        const SkPoint center = { kRadius, kRadius };
109        for (int i = 0; i < 6; ++i) {
110            SkScalar angle = 2 * SK_ScalarPI * i / 6;
111            SkPoint point;
112            point.fY = SkScalarSinCos(angle, &point.fX);
113            point.scale(kRadius);
114            point = center + point;
115            if (0 == i) {
116                hexagon.moveTo(point);
117            } else {
118                hexagon.lineTo(point);
119            }
120        }
121        fClips.addToTail()->setPath(hexagon);
122
123        SkMatrix scaleM;
124        scaleM.setScale(1.1f, 0.4f, kRadius, kRadius);
125        hexagon.transform(scaleM);
126        fClips.addToTail()->setPath(hexagon);
127
128        fClips.addToTail()->setRect(SkRect::MakeXYWH(8.3f, 11.6f, 78.2f, 72.6f));
129
130        SkPath rotRect;
131        SkRect rect = SkRect::MakeLTRB(10.f, 12.f, 80.f, 86.f);
132        rotRect.addRect(rect);
133        SkMatrix rotM;
134        rotM.setRotate(23.f, rect.centerX(), rect.centerY());
135        rotRect.transform(rotM);
136        fClips.addToTail()->setPath(rotRect);
137
138        fBmp = make_bmp(100, 100);
139    }
140
141    void onDraw(SkCanvas* canvas) override {
142        SkScalar y = 0;
143        constexpr SkScalar kMargin = 10.f;
144
145        SkPaint bgPaint;
146        bgPaint.setAlpha(0x15);
147        SkISize size = canvas->getBaseLayerSize();
148        canvas->drawBitmapRect(fBmp, SkRect::MakeIWH(size.fWidth, size.fHeight), &bgPaint);
149
150        constexpr char kTxt[] = "Clip Me!";
151        SkPaint txtPaint;
152        txtPaint.setTextSize(23.f);
153        txtPaint.setAntiAlias(true);
154        sk_tool_utils::set_portable_typeface(&txtPaint);
155        txtPaint.setColor(sk_tool_utils::color_to_565(SK_ColorDKGRAY));
156        SkScalar textW = txtPaint.measureText(kTxt, SK_ARRAY_COUNT(kTxt)-1);
157
158        SkScalar startX = 0;
159        int testLayers = kBench_Mode != this->getMode();
160        for (int doLayer = 0; doLayer <= testLayers; ++doLayer) {
161            for (ClipList::Iter iter(fClips, ClipList::Iter::kHead_IterStart);
162                 iter.get();
163                 iter.next()) {
164                const Clip* clip = iter.get();
165                SkScalar x = startX;
166                for (int aa = 0; aa < 2; ++aa) {
167                    if (doLayer) {
168                        SkRect bounds;
169                        clip->getBounds(&bounds);
170                        bounds.outset(2, 2);
171                        bounds.offset(x, y);
172                        canvas->saveLayer(&bounds, nullptr);
173                    } else {
174                        canvas->save();
175                    }
176                    canvas->translate(x, y);
177                    clip->setOnCanvas(canvas, kIntersect_SkClipOp, SkToBool(aa));
178                    canvas->drawBitmap(fBmp, 0, 0);
179                    canvas->restore();
180                    x += fBmp.width() + kMargin;
181                }
182                for (int aa = 0; aa < 2; ++aa) {
183
184                    SkPaint clipOutlinePaint;
185                    clipOutlinePaint.setAntiAlias(true);
186                    clipOutlinePaint.setColor(0x50505050);
187                    clipOutlinePaint.setStyle(SkPaint::kStroke_Style);
188                    clipOutlinePaint.setStrokeWidth(0);
189
190                    if (doLayer) {
191                        SkRect bounds;
192                        clip->getBounds(&bounds);
193                        bounds.outset(2, 2);
194                        bounds.offset(x, y);
195                        canvas->saveLayer(&bounds, nullptr);
196                    } else {
197                        canvas->save();
198                    }
199                    canvas->translate(x, y);
200                    SkPath closedClipPath;
201                    clip->asClosedPath(&closedClipPath);
202                    canvas->drawPath(closedClipPath, clipOutlinePaint);
203                    clip->setOnCanvas(canvas, kIntersect_SkClipOp, SkToBool(aa));
204                    canvas->scale(1.f, 1.8f);
205                    canvas->drawText(kTxt, SK_ARRAY_COUNT(kTxt)-1,
206                                     0, 1.5f * txtPaint.getTextSize(),
207                                     txtPaint);
208                    canvas->restore();
209                    x += textW + 2 * kMargin;
210                }
211                y += fBmp.height() + kMargin;
212            }
213            y = 0;
214            startX += 2 * fBmp.width() + SkScalarCeilToInt(2 * textW) + 6 * kMargin;
215        }
216    }
217
218    bool runAsBench() const override { return true; }
219
220private:
221    class Clip {
222    public:
223        enum ClipType {
224            kNone_ClipType,
225            kPath_ClipType,
226            kRect_ClipType
227        };
228
229        Clip () : fClipType(kNone_ClipType) {}
230
231        void setOnCanvas(SkCanvas* canvas, SkClipOp op, bool aa) const {
232            switch (fClipType) {
233                case kPath_ClipType:
234                    canvas->clipPath(fPath, op, aa);
235                    break;
236                case kRect_ClipType:
237                    canvas->clipRect(fRect, op, aa);
238                    break;
239                case kNone_ClipType:
240                    SkDEBUGFAIL("Uninitialized Clip.");
241                    break;
242            }
243        }
244
245        void asClosedPath(SkPath* path) const {
246            switch (fClipType) {
247                case kPath_ClipType:
248                    *path = fPath;
249                    path->close();
250                    break;
251                case kRect_ClipType:
252                    path->reset();
253                    path->addRect(fRect);
254                    break;
255                case kNone_ClipType:
256                    SkDEBUGFAIL("Uninitialized Clip.");
257                    break;
258            }
259        }
260
261        void setPath(const SkPath& path) {
262            fClipType = kPath_ClipType;
263            fPath = path;
264        }
265
266        void setRect(const SkRect& rect) {
267            fClipType = kRect_ClipType;
268            fRect = rect;
269            fPath.reset();
270        }
271
272        ClipType getType() const { return fClipType; }
273
274        void getBounds(SkRect* bounds) const {
275            switch (fClipType) {
276                case kPath_ClipType:
277                    *bounds = fPath.getBounds();
278                    break;
279                case kRect_ClipType:
280                    *bounds = fRect;
281                    break;
282                case kNone_ClipType:
283                    SkDEBUGFAIL("Uninitialized Clip.");
284                    break;
285            }
286        }
287
288    private:
289        ClipType fClipType;
290        SkPath fPath;
291        SkRect fRect;
292    };
293
294    typedef SkTLList<Clip, 1> ClipList;
295    ClipList         fClips;
296    SkBitmap         fBmp;
297
298    typedef GM INHERITED;
299};
300
301DEF_GM(return new ConvexPolyClip;)
302}
303