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