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