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