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
19static 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
54static 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
89static 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
117static 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
126static 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
156static 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
163static 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
175static 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
190class RegionView : public SampleView {
191public:
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
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
207    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
208        build_base_rgn(rgn);
209        rgn->op(fRect, op);
210    }
211
212
213protected:
214    // overrides from SkEventSink
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
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
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
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
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
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
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
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
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
402private:
403    SkIRect    fBase, fRect;
404
405    typedef SampleView INHERITED;
406};
407
408//////////////////////////////////////////////////////////////////////////////
409
410static SkView* MyFactory() { return new RegionView; }
411static SkViewRegister reg(MyFactory);
412