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