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 "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkFont.h"
11 #include "include/core/SkFontMetrics.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkRegion.h"
14 #include "include/core/SkShader.h"
15 #include "include/effects/SkGradientShader.h"
16 #include "samplecode/Sample.h"
17 #include "src/utils/SkUTF.h"
18
19 #include <math.h>
20
test_strokerect(SkCanvas * canvas)21 static void test_strokerect(SkCanvas* canvas) {
22 int width = 100;
23 int height = 100;
24
25 SkBitmap bitmap;
26 bitmap.allocPixels(SkImageInfo::MakeA8(width*2, height*2));
27 bitmap.eraseColor(SK_ColorTRANSPARENT);
28
29 SkScalar dx = 20;
30 SkScalar dy = 20;
31
32 SkPath path;
33 path.addRect(0.0f, 0.0f,
34 SkIntToScalar(width), SkIntToScalar(height),
35 SkPath::kCW_Direction);
36 SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
37
38 SkCanvas c(bitmap);
39 c.translate(dx, dy);
40
41 SkPaint paint;
42 paint.setStyle(SkPaint::kStroke_Style);
43 paint.setStrokeWidth(1);
44
45 // use the rect
46 c.clear(SK_ColorTRANSPARENT);
47 c.drawRect(r, paint);
48 canvas->drawBitmap(bitmap, 0, 0, nullptr);
49
50 // use the path
51 c.clear(SK_ColorTRANSPARENT);
52 c.drawPath(path, paint);
53 canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, nullptr);
54 }
55
drawFadingText(SkCanvas * canvas,const char * text,size_t len,SkScalar x,SkScalar y,const SkFont & font,const SkPaint & paint)56 static void drawFadingText(SkCanvas* canvas,
57 const char* text, size_t len, SkScalar x, SkScalar y,
58 const SkFont& font, const SkPaint& paint) {
59 // Need a bounds for the text
60 SkRect bounds;
61 SkFontMetrics fm;
62
63 font.getMetrics(&fm);
64 bounds.set(x, y + fm.fTop, x + font.measureText(text, len, SkTextEncoding::kUTF8), y + fm.fBottom);
65
66 // may need to outset bounds a little, to account for hinting and/or
67 // antialiasing
68 bounds.inset(-SkIntToScalar(2), -SkIntToScalar(2));
69
70 canvas->saveLayer(&bounds, nullptr);
71 canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint);
72
73 const SkPoint pts[] = {
74 { bounds.fLeft, y },
75 { bounds.fRight, y }
76 };
77 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
78
79 // pos[1] value is where we start to fade, relative to the width
80 // of our pts[] array.
81 const SkScalar pos[] = { 0, 0.9f, SK_Scalar1 };
82
83 SkPaint p;
84 p.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 3, SkTileMode::kClamp));
85 p.setBlendMode(SkBlendMode::kDstIn);
86 canvas->drawRect(bounds, p);
87
88 canvas->restore();
89 }
90
test_text(SkCanvas * canvas)91 static void test_text(SkCanvas* canvas) {
92 SkPaint paint;
93 paint.setAntiAlias(true);
94
95 SkFont font;
96 font.setSize(20);
97
98 const char* str = "Hamburgefons";
99 size_t len = strlen(str);
100 SkScalar x = 20;
101 SkScalar y = 20;
102
103 canvas->drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
104
105 y += 20;
106
107 const SkPoint pts[] = { { x , y },
108 { x + font.measureText(str, len, SkTextEncoding::kUTF8), y } };
109 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
110 const SkScalar pos[] = { 0, 0.9f, 1 };
111 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos,
112 SK_ARRAY_COUNT(colors),
113 SkTileMode::kClamp));
114 canvas->drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
115
116 y += 20;
117 paint.setShader(nullptr);
118 drawFadingText(canvas, str, len, x, y, font, paint);
119 }
120
scale_rect(SkIRect * dst,const SkIRect & src,float scale)121 static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
122 dst->fLeft = (int)::roundf(src.fLeft * scale);
123 dst->fTop = (int)::roundf(src.fTop * scale);
124 dst->fRight = (int)::roundf(src.fRight * scale);
125 dst->fBottom = (int)::roundf(src.fBottom * scale);
126 }
127
scale_rgn(SkRegion * dst,const SkRegion & src,float scale)128 static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
129 SkRegion tmp;
130 SkRegion::Iterator iter(src);
131
132 for (; !iter.done(); iter.next()) {
133 SkIRect r;
134 scale_rect(&r, iter.rect(), scale);
135 tmp.op(r, SkRegion::kUnion_Op);
136 }
137 dst->swap(tmp);
138 }
139
paint_rgn(SkCanvas * canvas,const SkRegion & rgn,const SkPaint & paint)140 static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
141 const SkPaint& paint) {
142 SkRegion scaled;
143 scale_rgn(&scaled, rgn, 0.5f);
144
145 SkRegion::Iterator iter(rgn);
146
147 for (; !iter.done(); iter.next())
148 {
149 SkRect r;
150 r.set(iter.rect());
151 canvas->drawRect(r, paint);
152 }
153 }
154
155 class RegionView : public Sample {
156 public:
RegionView()157 RegionView() {
158 fBase.set(100, 100, 150, 150);
159 fRect = fBase;
160 fRect.inset(5, 5);
161 fRect.offset(25, 25);
162 this->setBGColor(0xFFDDDDDD);
163 }
164
build_base_rgn(SkRegion * rgn)165 void build_base_rgn(SkRegion* rgn) {
166 rgn->setRect(fBase);
167 SkIRect r = fBase;
168 r.offset(75, 20);
169 rgn->op(r, SkRegion::kUnion_Op);
170 }
171
build_rgn(SkRegion * rgn,SkRegion::Op op)172 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
173 build_base_rgn(rgn);
174 rgn->op(fRect, op);
175 }
176
177
178 protected:
name()179 SkString name() override { return SkString("Regions"); }
180
drawstr(SkCanvas * canvas,const char text[],const SkPoint & loc,bool hilite)181 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
182 bool hilite) {
183 SkPaint paint;
184 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
185 SkFont font;
186 font.setSize(SkIntToScalar(20));
187 canvas->drawSimpleText(text, strlen(text), SkTextEncoding::kUTF8, loc.fX, loc.fY, font, paint);
188 }
189
drawPredicates(SkCanvas * canvas,const SkPoint pts[])190 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
191 SkRegion rgn;
192 build_base_rgn(&rgn);
193
194 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
195 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
196 }
197
drawOrig(SkCanvas * canvas,bool bg)198 void drawOrig(SkCanvas* canvas, bool bg) {
199 SkRect r;
200 SkPaint paint;
201
202 paint.setStyle(SkPaint::kStroke_Style);
203 if (bg)
204 paint.setColor(0xFFBBBBBB);
205
206 SkRegion rgn;
207 build_base_rgn(&rgn);
208 paint_rgn(canvas, rgn, paint);
209
210 r.set(fRect);
211 canvas->drawRect(r, paint);
212 }
213
drawRgnOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)214 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
215 SkRegion rgn;
216
217 this->build_rgn(&rgn, op);
218
219 {
220 SkRegion tmp, tmp2(rgn);
221
222 tmp = tmp2;
223 tmp.translate(5, -3);
224
225 {
226 char buffer[1000];
227 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr);
228 SkASSERT(size <= sizeof(buffer));
229 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer);
230 SkASSERT(size == size2);
231
232 SkRegion tmp3;
233 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
234 SkASSERT(size == size2);
235
236 SkASSERT(tmp3 == tmp);
237 }
238
239 rgn.translate(20, 30, &tmp);
240 SkASSERT(rgn.isEmpty() || tmp != rgn);
241 tmp.translate(-20, -30);
242 SkASSERT(tmp == rgn);
243 }
244
245 this->drawOrig(canvas, true);
246
247 SkPaint paint;
248 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
249 paint_rgn(canvas, rgn, paint);
250
251 paint.setStyle(SkPaint::kStroke_Style);
252 paint.setColor(color);
253 paint_rgn(canvas, rgn, paint);
254 }
255
drawPathOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)256 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
257 SkRegion rgn;
258 SkPath path;
259
260 this->build_rgn(&rgn, op);
261 rgn.getBoundaryPath(&path);
262
263 this->drawOrig(canvas, true);
264
265 SkPaint paint;
266
267 paint.setStyle(SkPaint::kFill_Style);
268 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
269 canvas->drawPath(path, paint);
270 paint.setColor(color);
271 paint.setStyle(SkPaint::kStroke_Style);
272 canvas->drawPath(path, paint);
273 }
274
onDrawContent(SkCanvas * canvas)275 void onDrawContent(SkCanvas* canvas) override {
276 if (false) { // avoid bit rot, suppress warning
277 test_strokerect(canvas);
278 return;
279 }
280 if (false) { // avoid bit rot, suppress warning
281 test_text(canvas);
282 return;
283 }
284
285 const SkPoint origins[] = {
286 { 30*SK_Scalar1, 50*SK_Scalar1 },
287 { 150*SK_Scalar1, 50*SK_Scalar1 },
288 };
289 this->drawPredicates(canvas, origins);
290
291 static const struct {
292 SkColor fColor;
293 const char* fName;
294 SkRegion::Op fOp;
295 } gOps[] = {
296 { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
297 { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
298 { 0xFF008800, "Union", SkRegion::kUnion_Op },
299 { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
300 };
301
302 SkFont font;
303 font.setSize(SK_Scalar1*24);
304
305 this->drawOrig(canvas, false);
306 canvas->save();
307 canvas->translate(SkIntToScalar(200), 0);
308 this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
309 canvas->restore();
310
311 canvas->translate(0, SkIntToScalar(200));
312
313 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
314 canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8,
315 SkIntToScalar(75), SkIntToScalar(50), font, SkPaint());
316
317 this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
318
319 canvas->save();
320 canvas->translate(0, SkIntToScalar(200));
321 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
322 canvas->restore();
323
324 canvas->translate(SkIntToScalar(200), 0);
325 }
326 }
327
onFindClickHandler(SkScalar x,SkScalar y,ModifierKey modi)328 virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
329 ModifierKey modi) override {
330 return fRect.contains(SkScalarRoundToInt(x),
331 SkScalarRoundToInt(y)) ? new Click() : nullptr;
332 }
333
onClick(Click * click)334 bool onClick(Click* click) override {
335 fRect.offset(click->fCurr.fX - click->fPrev.fX,
336 click->fCurr.fY - click->fPrev.fY);
337 return true;
338 }
339
340 private:
341 SkIRect fBase, fRect;
342
343 typedef Sample INHERITED;
344 };
345
346 //////////////////////////////////////////////////////////////////////////////
347
348 DEF_SAMPLE( return new RegionView(); )
349