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 "Sample.h"
9 #include "SkBitmap.h"
10 #include "SkCanvas.h"
11 #include "SkFont.h"
12 #include "SkFontMetrics.h"
13 #include "SkGradientShader.h"
14 #include "SkPath.h"
15 #include "SkRegion.h"
16 #include "SkShader.h"
17 #include "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, kUTF8_SkTextEncoding), 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, kUTF8_SkTextEncoding, 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, SkShader::kClamp_TileMode));
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, kUTF8_SkTextEncoding, x, y, font, paint);
104 
105     y += 20;
106 
107     const SkPoint pts[] = { { x, y }, { x + font.measureText(str, len, kUTF8_SkTextEncoding), y } };
108     const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
109     const SkScalar pos[] = { 0, 0.9f, 1 };
110     paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos,
111                                                  SK_ARRAY_COUNT(colors),
112                                                  SkShader::kClamp_TileMode));
113     canvas->drawSimpleText(str, len, kUTF8_SkTextEncoding, x, y, font, paint);
114 
115     y += 20;
116     paint.setShader(nullptr);
117     drawFadingText(canvas, str, len, x, y, font, paint);
118 }
119 
scale_rect(SkIRect * dst,const SkIRect & src,float scale)120 static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
121     dst->fLeft = (int)::roundf(src.fLeft * scale);
122     dst->fTop = (int)::roundf(src.fTop * scale);
123     dst->fRight = (int)::roundf(src.fRight * scale);
124     dst->fBottom = (int)::roundf(src.fBottom * scale);
125 }
126 
scale_rgn(SkRegion * dst,const SkRegion & src,float scale)127 static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
128     SkRegion tmp;
129     SkRegion::Iterator iter(src);
130 
131     for (; !iter.done(); iter.next()) {
132         SkIRect r;
133         scale_rect(&r, iter.rect(), scale);
134         tmp.op(r, SkRegion::kUnion_Op);
135     }
136     dst->swap(tmp);
137 }
138 
paint_rgn(SkCanvas * canvas,const SkRegion & rgn,const SkPaint & paint)139 static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
140                       const SkPaint& paint) {
141     SkRegion scaled;
142     scale_rgn(&scaled, rgn, 0.5f);
143 
144     SkRegion::Iterator  iter(rgn);
145 
146     for (; !iter.done(); iter.next())
147     {
148         SkRect    r;
149         r.set(iter.rect());
150         canvas->drawRect(r, paint);
151     }
152 }
153 
154 class RegionView : public Sample {
155 public:
RegionView()156     RegionView() {
157         fBase.set(100, 100, 150, 150);
158         fRect = fBase;
159         fRect.inset(5, 5);
160         fRect.offset(25, 25);
161         this->setBGColor(0xFFDDDDDD);
162     }
163 
build_base_rgn(SkRegion * rgn)164     void build_base_rgn(SkRegion* rgn) {
165         rgn->setRect(fBase);
166         SkIRect r = fBase;
167         r.offset(75, 20);
168         rgn->op(r, SkRegion::kUnion_Op);
169     }
170 
build_rgn(SkRegion * rgn,SkRegion::Op op)171     void build_rgn(SkRegion* rgn, SkRegion::Op op) {
172         build_base_rgn(rgn);
173         rgn->op(fRect, op);
174     }
175 
176 
177 protected:
onQuery(Sample::Event * evt)178     bool onQuery(Sample::Event* evt) override {
179         if (Sample::TitleQ(*evt)) {
180             Sample::TitleR(evt, "Regions");
181             return true;
182         }
183         return this->INHERITED::onQuery(evt);
184     }
185 
drawstr(SkCanvas * canvas,const char text[],const SkPoint & loc,bool hilite)186     static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
187                         bool hilite) {
188         SkPaint paint;
189         paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
190         SkFont font;
191         font.setSize(SkIntToScalar(20));
192         canvas->drawSimpleText(text, strlen(text), kUTF8_SkTextEncoding, loc.fX, loc.fY, font, paint);
193     }
194 
drawPredicates(SkCanvas * canvas,const SkPoint pts[])195     void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
196         SkRegion rgn;
197         build_base_rgn(&rgn);
198 
199         drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
200         drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
201     }
202 
drawOrig(SkCanvas * canvas,bool bg)203     void drawOrig(SkCanvas* canvas, bool bg) {
204         SkRect      r;
205         SkPaint     paint;
206 
207         paint.setStyle(SkPaint::kStroke_Style);
208         if (bg)
209             paint.setColor(0xFFBBBBBB);
210 
211         SkRegion rgn;
212         build_base_rgn(&rgn);
213         paint_rgn(canvas, rgn, paint);
214 
215         r.set(fRect);
216         canvas->drawRect(r, paint);
217     }
218 
drawRgnOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)219     void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
220         SkRegion    rgn;
221 
222         this->build_rgn(&rgn, op);
223 
224         {
225             SkRegion tmp, tmp2(rgn);
226 
227             tmp = tmp2;
228             tmp.translate(5, -3);
229 
230             {
231                 char    buffer[1000];
232                 SkDEBUGCODE(size_t  size = ) tmp.writeToMemory(nullptr);
233                 SkASSERT(size <= sizeof(buffer));
234                 SkDEBUGCODE(size_t  size2 = ) tmp.writeToMemory(buffer);
235                 SkASSERT(size == size2);
236 
237                 SkRegion    tmp3;
238                 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
239                 SkASSERT(size == size2);
240 
241                 SkASSERT(tmp3 == tmp);
242             }
243 
244             rgn.translate(20, 30, &tmp);
245             SkASSERT(rgn.isEmpty() || tmp != rgn);
246             tmp.translate(-20, -30);
247             SkASSERT(tmp == rgn);
248         }
249 
250         this->drawOrig(canvas, true);
251 
252         SkPaint paint;
253         paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
254         paint_rgn(canvas, rgn, paint);
255 
256         paint.setStyle(SkPaint::kStroke_Style);
257         paint.setColor(color);
258         paint_rgn(canvas, rgn, paint);
259     }
260 
drawPathOped(SkCanvas * canvas,SkRegion::Op op,SkColor color)261     void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
262         SkRegion    rgn;
263         SkPath      path;
264 
265         this->build_rgn(&rgn, op);
266         rgn.getBoundaryPath(&path);
267 
268         this->drawOrig(canvas, true);
269 
270         SkPaint paint;
271 
272         paint.setStyle(SkPaint::kFill_Style);
273         paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
274         canvas->drawPath(path, paint);
275         paint.setColor(color);
276         paint.setStyle(SkPaint::kStroke_Style);
277         canvas->drawPath(path, paint);
278     }
279 
onDrawContent(SkCanvas * canvas)280     void onDrawContent(SkCanvas* canvas) override {
281         if (false) { // avoid bit rot, suppress warning
282             test_strokerect(canvas);
283             return;
284         }
285         if (false) { // avoid bit rot, suppress warning
286             test_text(canvas);
287             return;
288         }
289 
290         const SkPoint origins[] = {
291             { 30*SK_Scalar1, 50*SK_Scalar1 },
292             { 150*SK_Scalar1, 50*SK_Scalar1 },
293         };
294         this->drawPredicates(canvas, origins);
295 
296         static const struct {
297             SkColor         fColor;
298             const char*     fName;
299             SkRegion::Op    fOp;
300         } gOps[] = {
301             { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
302             { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
303             { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
304             { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
305         };
306 
307         SkFont font;
308         font.setSize(SK_Scalar1*24);
309 
310         this->drawOrig(canvas, false);
311         canvas->save();
312             canvas->translate(SkIntToScalar(200), 0);
313             this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
314         canvas->restore();
315 
316         canvas->translate(0, SkIntToScalar(200));
317 
318         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
319             canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), kUTF8_SkTextEncoding,
320                                    SkIntToScalar(75), SkIntToScalar(50), font, SkPaint());
321 
322             this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
323 
324             canvas->save();
325             canvas->translate(0, SkIntToScalar(200));
326             this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
327             canvas->restore();
328 
329             canvas->translate(SkIntToScalar(200), 0);
330         }
331     }
332 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)333     virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y,
334                                               unsigned modi) override {
335         return fRect.contains(SkScalarRoundToInt(x),
336                               SkScalarRoundToInt(y)) ? new Click(this) : nullptr;
337     }
338 
onClick(Click * click)339     bool onClick(Click* click) override {
340         fRect.offset(click->fICurr.fX - click->fIPrev.fX,
341                      click->fICurr.fY - click->fIPrev.fY);
342         return true;
343     }
344 
345 private:
346     SkIRect    fBase, fRect;
347 
348     typedef Sample INHERITED;
349 };
350 
351 //////////////////////////////////////////////////////////////////////////////
352 
353 DEF_SAMPLE( return new RegionView(); )
354