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