1/*
2 * Copyright 2011 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#include "SkCanvas.h"
11#include "SkPath.h"
12
13namespace skiagm {
14
15constexpr SkColor gPathColor = SK_ColorBLACK;
16constexpr SkColor gClipAColor = SK_ColorBLUE;
17constexpr SkColor gClipBColor = SK_ColorRED;
18
19class ComplexClipGM : public GM {
20public:
21    ComplexClipGM(bool aaclip, bool saveLayer, bool invertDraw)
22    : fDoAAClip(aaclip)
23    , fDoSaveLayer(saveLayer)
24    , fInvertDraw(invertDraw) {
25        this->setBGColor(0xFFDEDFDE);
26    }
27
28protected:
29
30
31    SkString onShortName() {
32        SkString str;
33        str.printf("complexclip_%s%s%s",
34                   fDoAAClip ? "aa" : "bw",
35                   fDoSaveLayer ? "_layer" : "",
36                   fInvertDraw ? "_invert" : "");
37        return str;
38    }
39
40    SkISize onISize() { return SkISize::Make(970, 780); }
41
42    virtual void onDraw(SkCanvas* canvas) {
43        SkPath path;
44        path.moveTo(SkIntToScalar(0),   SkIntToScalar(50));
45        path.quadTo(SkIntToScalar(0),   SkIntToScalar(0),   SkIntToScalar(50),  SkIntToScalar(0));
46        path.lineTo(SkIntToScalar(175), SkIntToScalar(0));
47        path.quadTo(SkIntToScalar(200), SkIntToScalar(0),   SkIntToScalar(200), SkIntToScalar(25));
48        path.lineTo(SkIntToScalar(200), SkIntToScalar(150));
49        path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200));
50        path.lineTo(SkIntToScalar(0),   SkIntToScalar(200));
51        path.close();
52        path.moveTo(SkIntToScalar(50),  SkIntToScalar(50));
53        path.lineTo(SkIntToScalar(150), SkIntToScalar(50));
54        path.lineTo(SkIntToScalar(150), SkIntToScalar(125));
55        path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150));
56        path.lineTo(SkIntToScalar(50),  SkIntToScalar(150));
57        path.close();
58        if (fInvertDraw) {
59            path.setFillType(SkPath::kInverseEvenOdd_FillType);
60        } else {
61            path.setFillType(SkPath::kEvenOdd_FillType);
62        }
63        SkPaint pathPaint;
64        pathPaint.setAntiAlias(true);
65        pathPaint.setColor(gPathColor);
66
67        SkPath clipA;
68        clipA.moveTo(SkIntToScalar(10),  SkIntToScalar(20));
69        clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22));
70        clipA.lineTo(SkIntToScalar(70),  SkIntToScalar(105));
71        clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177));
72        clipA.lineTo(SkIntToScalar(-5),  SkIntToScalar(180));
73        clipA.close();
74
75        SkPath clipB;
76        clipB.moveTo(SkIntToScalar(40),  SkIntToScalar(10));
77        clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15));
78        clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190));
79        clipB.lineTo(SkIntToScalar(40),  SkIntToScalar(185));
80        clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100));
81        clipB.close();
82
83        SkPaint paint;
84        paint.setAntiAlias(true);
85        sk_tool_utils::set_portable_typeface(&paint);
86        paint.setTextSize(SkIntToScalar(20));
87
88        constexpr struct {
89            SkClipOp fOp;
90            const char*      fName;
91        } gOps[] = { //extra spaces in names for measureText
92            {kIntersect_SkClipOp,         "Isect "},
93            {kDifference_SkClipOp,        "Diff " },
94            {kUnion_SkClipOp,             "Union "},
95            {kXOR_SkClipOp,               "Xor "  },
96            {kReverseDifference_SkClipOp, "RDiff "}
97        };
98
99        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
100        canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);
101
102        if (fDoSaveLayer) {
103            // We want the layer to appear symmetric relative to actual
104            // device boundaries so we need to "undo" the effect of the
105            // scale and translate
106            SkRect bounds = SkRect::MakeLTRB(
107              4.0f/3.0f * -20,
108              4.0f/3.0f * -20,
109              4.0f/3.0f * (this->getISize().fWidth - 20),
110              4.0f/3.0f * (this->getISize().fHeight - 20));
111
112            bounds.inset(SkIntToScalar(100), SkIntToScalar(100));
113            SkPaint boundPaint;
114            boundPaint.setColor(SK_ColorRED);
115            boundPaint.setStyle(SkPaint::kStroke_Style);
116            canvas->drawRect(bounds, boundPaint);
117            canvas->saveLayer(&bounds, nullptr);
118        }
119
120        for (int invBits = 0; invBits < 4; ++invBits) {
121            canvas->save();
122            for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
123                this->drawHairlines(canvas, path, clipA, clipB);
124
125                bool doInvA = SkToBool(invBits & 1);
126                bool doInvB = SkToBool(invBits & 2);
127                canvas->save();
128                    // set clip
129                    clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
130                                      SkPath::kEvenOdd_FillType);
131                    clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
132                                      SkPath::kEvenOdd_FillType);
133                    canvas->clipPath(clipA, fDoAAClip);
134                    canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip);
135
136                    // In the inverse case we need to prevent the draw from covering the whole
137                    // canvas.
138                    if (fInvertDraw) {
139                        SkRect rectClip = clipA.getBounds();
140                        rectClip.join(path.getBounds());
141                        rectClip.join(path.getBounds());
142                        rectClip.outset(5, 5);
143                        canvas->clipRect(rectClip);
144                    }
145
146                    // draw path clipped
147                    canvas->drawPath(path, pathPaint);
148                canvas->restore();
149
150
151                SkScalar txtX = SkIntToScalar(45);
152                paint.setColor(gClipAColor);
153                const char* aTxt = doInvA ? "InvA " : "A ";
154                canvas->drawString(aTxt, txtX, SkIntToScalar(220), paint);
155                txtX += paint.measureText(aTxt, strlen(aTxt));
156                paint.setColor(SK_ColorBLACK);
157                canvas->drawString(gOps[op].fName, txtX, SkIntToScalar(220), paint);
158                txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName));
159                paint.setColor(gClipBColor);
160                const char* bTxt = doInvB ? "InvB " : "B ";
161                canvas->drawString(bTxt, txtX, SkIntToScalar(220), paint);
162
163                canvas->translate(SkIntToScalar(250),0);
164            }
165            canvas->restore();
166            canvas->translate(0, SkIntToScalar(250));
167        }
168
169        if (fDoSaveLayer) {
170            canvas->restore();
171        }
172    }
173private:
174    void drawHairlines(SkCanvas* canvas, const SkPath& path,
175                       const SkPath& clipA, const SkPath& clipB) {
176        SkPaint paint;
177        paint.setAntiAlias(true);
178        paint.setStyle(SkPaint::kStroke_Style);
179        const SkAlpha fade = 0x33;
180
181        // draw path in hairline
182        paint.setColor(gPathColor); paint.setAlpha(fade);
183        canvas->drawPath(path, paint);
184
185        // draw clips in hair line
186        paint.setColor(gClipAColor); paint.setAlpha(fade);
187        canvas->drawPath(clipA, paint);
188        paint.setColor(gClipBColor); paint.setAlpha(fade);
189        canvas->drawPath(clipB, paint);
190    }
191
192    bool fDoAAClip;
193    bool fDoSaveLayer;
194    bool fInvertDraw;
195
196    typedef GM INHERITED;
197};
198
199//////////////////////////////////////////////////////////////////////////////
200
201DEF_GM(return new ComplexClipGM(false, false, false);)
202DEF_GM(return new ComplexClipGM(false, false, true);)
203DEF_GM(return new ComplexClipGM(false, true, false);)
204DEF_GM(return new ComplexClipGM(false, true, true);)
205DEF_GM(return new ComplexClipGM(true, false, false);)
206DEF_GM(return new ComplexClipGM(true, false, true);)
207DEF_GM(return new ComplexClipGM(true, true, false);)
208DEF_GM(return new ComplexClipGM(true, true, true);)
209}
210