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