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