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