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 #include "SampleCode.h"
8 #include "SkView.h"
9 #include "SkCanvas.h"
10 #include "SkGradientShader.h"
11 #include "SkPath.h"
12 #include "SkRegion.h"
13 #include "SkShader.h"
14 #include "SkUtils.h"
15 #include "SkImageDecoder.h"
16
17 #include <math.h>
18
test_strokerect(SkCanvas * canvas)19 static void test_strokerect(SkCanvas* canvas) {
20 int width = 100;
21 int height = 100;
22
23 SkBitmap bitmap;
24 bitmap.allocPixels(SkImageInfo::MakeA8(width*2, height*2));
25 bitmap.eraseColor(SK_ColorTRANSPARENT);
26
27 SkScalar dx = 20;
28 SkScalar dy = 20;
29
30 SkPath path;
31 path.addRect(0.0f, 0.0f,
32 SkIntToScalar(width), SkIntToScalar(height),
33 SkPath::kCW_Direction);
34 SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
35
36 SkCanvas c(bitmap);
37 c.translate(dx, dy);
38
39 SkPaint paint;
40 paint.setStyle(SkPaint::kStroke_Style);
41 paint.setStrokeWidth(1);
42
43 // use the rect
44 c.clear(SK_ColorTRANSPARENT);
45 c.drawRect(r, paint);
46 canvas->drawBitmap(bitmap, 0, 0, nullptr);
47
48 // use the path
49 c.clear(SK_ColorTRANSPARENT);
50 c.drawPath(path, paint);
51 canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, nullptr);
52 }
53
drawFadingText(SkCanvas * canvas,const char * text,size_t len,SkScalar x,SkScalar y,const SkPaint & paint)54 static void drawFadingText(SkCanvas* canvas,
55 const char* text, size_t len, SkScalar x, SkScalar y,
56 const SkPaint& paint) {
57 // Need a bounds for the text
58 SkRect bounds;
59 SkPaint::FontMetrics fm;
60
61 paint.getFontMetrics(&fm);
62 bounds.set(x, y + fm.fTop, x + paint.measureText(text, len), y + fm.fBottom);
63
64 // may need to outset bounds a little, to account for hinting and/or
65 // antialiasing
66 bounds.inset(-SkIntToScalar(2), -SkIntToScalar(2));
67
68 canvas->saveLayer(&bounds, nullptr);
69 canvas->drawText(text, len, x, y, paint);
70
71 const SkPoint pts[] = {
72 { bounds.fLeft, y },
73 { bounds.fRight, y }
74 };
75 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
76
77 // pos[1] value is where we start to fade, relative to the width
78 // of our pts[] array.
79 const SkScalar pos[] = { 0, 0.9f, SK_Scalar1 };
80
81 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 3,
82 SkShader::kClamp_TileMode);
83 SkPaint p;
84 p.setShader(s)->unref();
85 p.setXfermodeMode(SkXfermode::kDstIn_Mode);
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 paint.setTextSize(20);
95
96 const char* str = "Hamburgefons";
97 size_t len = strlen(str);
98 SkScalar x = 20;
99 SkScalar y = 20;
100
101 canvas->drawText(str, len, x, y, paint);
102
103 y += 20;
104
105 const SkPoint pts[] = { { x, y }, { x + paint.measureText(str, len), y } };
106 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
107 const SkScalar pos[] = { 0, 0.9f, 1 };
108 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos,
109 SK_ARRAY_COUNT(colors),
110 SkShader::kClamp_TileMode);
111 paint.setShader(s)->unref();
112 canvas->drawText(str, len, x, y, paint);
113
114 y += 20;
115 paint.setShader(nullptr);
116 drawFadingText(canvas, str, len, x, y, paint);
117 }
118
119 #ifdef SK_DEBUG
make_rgn(SkRegion * rgn,int left,int top,int right,int bottom,int count,int32_t runs[])120 static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
121 int count, int32_t runs[]) {
122 SkIRect r;
123 r.set(left, top, right, bottom);
124
125 rgn->debugSetRuns(runs, count);
126 SkASSERT(rgn->getBounds() == r);
127 }
128
test_union_bug_1505668(SkRegion * ra,SkRegion * rb,SkRegion * rc)129 static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
130 static int32_t dataA[] = {
131 0x00000001,
132 0x000001dd, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
133 0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
134 0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
135 0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
136 0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
137 0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
138 0x7fffffff
139 };
140 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
141
142 static int32_t dataB[] = {
143 0x000000b6,
144 0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
145 0x000000d6, 0, 0x7fffffff,
146 0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
147 0x000000e6, 0, 0x7fffffff,
148 0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
149 0x000000f6, 0, 0x7fffffff,
150 0x00000104, 1, 0x000000a1, 0x000000b0, 0x7fffffff,
151 0x7fffffff
152 };
153 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
154
155 rc->op(*ra, *rb, SkRegion::kUnion_Op);
156 }
157 #endif
158
scale_rect(SkIRect * dst,const SkIRect & src,float scale)159 static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
160 dst->fLeft = (int)::roundf(src.fLeft * scale);
161 dst->fTop = (int)::roundf(src.fTop * scale);
162 dst->fRight = (int)::roundf(src.fRight * scale);
163 dst->fBottom = (int)::roundf(src.fBottom * scale);
164 }
165
scale_rgn(SkRegion * dst,const SkRegion & src,float scale)166 static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
167 SkRegion tmp;
168 SkRegion::Iterator iter(src);
169
170 for (; !iter.done(); iter.next()) {
171 SkIRect r;
172 scale_rect(&r, iter.rect(), scale);
173 tmp.op(r, SkRegion::kUnion_Op);
174 }
175 dst->swap(tmp);
176 }
177
paint_rgn(SkCanvas * canvas,const SkRegion & rgn,const SkPaint & paint)178 static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
179 const SkPaint& paint) {
180 SkRegion scaled;
181 scale_rgn(&scaled, rgn, 0.5f);
182
183 SkRegion::Iterator iter(rgn);
184
185 for (; !iter.done(); iter.next())
186 {
187 SkRect r;
188 r.set(iter.rect());
189 canvas->drawRect(r, paint);
190 }
191 }
192
193 class RegionView : public SampleView {
194 public:
RegionView()195 RegionView() {
196 fBase.set(100, 100, 150, 150);
197 fRect = fBase;
198 fRect.inset(5, 5);
199 fRect.offset(25, 25);
200 this->setBGColor(0xFFDDDDDD);
201 }
202
build_base_rgn(SkRegion * rgn)203 void build_base_rgn(SkRegion* rgn) {
204 rgn->setRect(fBase);
205 SkIRect r = fBase;
206 r.offset(75, 20);
207 rgn->op(r, SkRegion::kUnion_Op);
208 }
209
build_rgn(SkRegion * rgn,SkRegion::Op op)210 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
211 build_base_rgn(rgn);
212 rgn->op(fRect, op);
213 }
214
215
216 protected:
217 // overrides from SkEventSink
onQuery(SkEvent * evt)218 bool onQuery(SkEvent* evt) override {
219 if (SampleCode::TitleQ(*evt)) {
220 SampleCode::TitleR(evt, "Regions");
221 return true;
222 }
223 return this->INHERITED::onQuery(evt);
224 }
225
drawstr(SkCanvas * canvas,const char text[],const SkPoint & loc,bool hilite)226 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
227 bool hilite) {
228 SkPaint paint;
229 paint.setAntiAlias(true);
230 paint.setTextSize(SkIntToScalar(20));
231 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
232 canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
233 }
234
drawPredicates(SkCanvas * canvas,const SkPoint pts[])235 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
236 SkRegion rgn;
237 build_base_rgn(&rgn);
238
239 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
240 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
241 }
242
drawOrig(SkCanvas * canvas,bool bg)243 void drawOrig(SkCanvas* canvas, bool bg) {
244 SkRect r;
245 SkPaint paint;
246
247 paint.setStyle(SkPaint::kStroke_Style);
248 if (bg)
249 paint.setColor(0xFFBBBBBB);
250
251 SkRegion rgn;
252 build_base_rgn(&rgn);
253 paint_rgn(canvas, rgn, paint);
254
255 r.set(fRect);
256 canvas->drawRect(r, paint);
257 }
258
drawRgnOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)259 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
260 SkRegion rgn;
261
262 this->build_rgn(&rgn, op);
263
264 {
265 SkRegion tmp, tmp2(rgn);
266
267 tmp = tmp2;
268 tmp.translate(5, -3);
269
270 {
271 char buffer[1000];
272 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr);
273 SkASSERT(size <= sizeof(buffer));
274 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer);
275 SkASSERT(size == size2);
276
277 SkRegion tmp3;
278 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
279 SkASSERT(size == size2);
280
281 SkASSERT(tmp3 == tmp);
282 }
283
284 rgn.translate(20, 30, &tmp);
285 SkASSERT(rgn.isEmpty() || tmp != rgn);
286 tmp.translate(-20, -30);
287 SkASSERT(tmp == rgn);
288 }
289
290 this->drawOrig(canvas, true);
291
292 SkPaint paint;
293 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
294 paint_rgn(canvas, rgn, paint);
295
296 paint.setStyle(SkPaint::kStroke_Style);
297 paint.setColor(color);
298 paint_rgn(canvas, rgn, paint);
299 }
300
drawPathOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)301 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
302 SkRegion rgn;
303 SkPath path;
304
305 this->build_rgn(&rgn, op);
306 rgn.getBoundaryPath(&path);
307
308 this->drawOrig(canvas, true);
309
310 SkPaint paint;
311
312 paint.setStyle(SkPaint::kFill_Style);
313 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
314 canvas->drawPath(path, paint);
315 paint.setColor(color);
316 paint.setStyle(SkPaint::kStroke_Style);
317 canvas->drawPath(path, paint);
318 }
319
onDrawContent(SkCanvas * canvas)320 void onDrawContent(SkCanvas* canvas) override {
321 if (false) { // avoid bit rot, suppress warning
322 test_strokerect(canvas);
323 return;
324 }
325 if (false) { // avoid bit rot, suppress warning
326 test_text(canvas);
327 return;
328 }
329 #ifdef SK_DEBUG
330 if (true) {
331 SkRegion a, b, c;
332 test_union_bug_1505668(&a, &b, &c);
333
334 if (false) { // draw the result of the test
335 SkPaint paint;
336
337 canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
338 paint.setColor(SK_ColorRED);
339 paint_rgn(canvas, a, paint);
340 paint.setColor(0x800000FF);
341 paint_rgn(canvas, b, paint);
342 paint.setColor(SK_ColorBLACK);
343 paint.setStyle(SkPaint::kStroke_Style);
344 // paint_rgn(canvas, c, paint);
345 return;
346 }
347 }
348 #endif
349 const SkPoint origins[] = {
350 { 30*SK_Scalar1, 50*SK_Scalar1 },
351 { 150*SK_Scalar1, 50*SK_Scalar1 },
352 };
353 this->drawPredicates(canvas, origins);
354
355 static const struct {
356 SkColor fColor;
357 const char* fName;
358 SkRegion::Op fOp;
359 } gOps[] = {
360 { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
361 { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
362 { 0xFF008800, "Union", SkRegion::kUnion_Op },
363 { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
364 };
365
366 SkPaint textPaint;
367 textPaint.setAntiAlias(true);
368 textPaint.setTextSize(SK_Scalar1*24);
369
370 this->drawOrig(canvas, false);
371 canvas->save();
372 canvas->translate(SkIntToScalar(200), 0);
373 this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
374 canvas->restore();
375
376 canvas->translate(0, SkIntToScalar(200));
377
378 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
379 canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
380
381 this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
382
383 canvas->save();
384 canvas->translate(0, SkIntToScalar(200));
385 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
386 canvas->restore();
387
388 canvas->translate(SkIntToScalar(200), 0);
389 }
390 }
391
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)392 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
393 unsigned modi) override {
394 return fRect.contains(SkScalarRoundToInt(x),
395 SkScalarRoundToInt(y)) ? new Click(this) : nullptr;
396 }
397
onClick(Click * click)398 bool onClick(Click* click) override {
399 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
400 click->fICurr.fY - click->fIPrev.fY);
401 this->inval(nullptr);
402 return true;
403 }
404
405 private:
406 SkIRect fBase, fRect;
407
408 typedef SampleView INHERITED;
409 };
410
411 //////////////////////////////////////////////////////////////////////////////
412
MyFactory()413 static SkView* MyFactory() { return new RegionView; }
414 static SkViewRegister reg(MyFactory);
415