blob: a46b488bf88794c93a2c5f023c720552f4d35a17 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#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
reed@google.com4b5894c2012-05-02 18:03:32 +000018static void test_strokerect(SkCanvas* canvas) {
19 int width = 100;
20 int height = 100;
21
22 SkBitmap bitmap;
23 bitmap.setConfig(SkBitmap::kA8_Config, width*2, height*2);
24 bitmap.allocPixels();
25 bitmap.eraseColor(0);
26
27 SkScalar dx = 20;
28 SkScalar dy = 20;
29
30 SkPath path;
31 path.addRect(0.0f, 0.0f, width, height, SkPath::kCW_Direction);
32 SkRect r = SkRect::MakeWH(width, height);
33
34 SkCanvas c(bitmap);
35 c.translate(dx, dy);
36
37 SkPaint paint;
38 paint.setStyle(SkPaint::kStroke_Style);
39 paint.setStrokeWidth(1);
40
41 // use the rect
42 c.clear(0);
43 c.drawRect(r, paint);
44 canvas->drawBitmap(bitmap, 0, 0, NULL);
45
46 // use the path
47 c.clear(0);
48 c.drawPath(path, paint);
49 canvas->drawBitmap(bitmap, 2*width, 0, NULL);
50}
51
52static void drawFadingText(SkCanvas* canvas,
53 const char* text, size_t len, SkScalar x, SkScalar y,
54 const SkPaint& paint) {
55 // Need a bounds for the text
56 SkRect bounds;
57 SkPaint::FontMetrics fm;
58
59 paint.getFontMetrics(&fm);
60 bounds.set(x, y + fm.fTop, x + paint.measureText(text, len), y + fm.fBottom);
61
62 // may need to outset bounds a little, to account for hinting and/or
63 // antialiasing
64 bounds.inset(-SkIntToScalar(2), -SkIntToScalar(2));
65
66 canvas->saveLayer(&bounds, NULL);
67 canvas->drawText(text, len, x, y, paint);
68
69 const SkPoint pts[] = {
70 { bounds.fLeft, y },
71 { bounds.fRight, y }
72 };
73 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
74
75 // pos[1] value is where we start to fade, relative to the width
76 // of our pts[] array.
77 const SkScalar pos[] = { 0, SkFloatToScalar(0.9), SK_Scalar1 };
78
79 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 3,
80 SkShader::kClamp_TileMode);
81 SkPaint p;
82 p.setShader(s)->unref();
83 p.setXfermodeMode(SkXfermode::kDstIn_Mode);
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 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos,
107 SK_ARRAY_COUNT(colors),
108 SkShader::kClamp_TileMode);
109 paint.setShader(s)->unref();
110 canvas->drawText(str, len, x, y, paint);
111
112 y += 20;
113 paint.setShader(NULL);
114 drawFadingText(canvas, str, len, x, y, paint);
115}
116
reed@android.comf2b98d62010-12-20 18:26:13 +0000117#ifdef SK_BUILD_FOR_WIN
118// windows doesn't have roundf
119inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
120#endif
121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122#ifdef SK_DEBUG
123static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
124 size_t count, int32_t runs[]) {
125 SkIRect r;
126 r.set(left, top, right, bottom);
127
128 rgn->debugSetRuns(runs, count);
129 SkASSERT(rgn->getBounds() == r);
130}
131
132static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
133 static int32_t dataA[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000134 0x00000001,
135 0x000001dd, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
136 0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
137 0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
138 0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
139 0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
140 0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
141 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 };
143 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
144
145 static int32_t dataB[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000146 0x000000b6,
147 0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
148 0x000000d6, 0, 0x7fffffff,
149 0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
150 0x000000e6, 0, 0x7fffffff,
151 0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
152 0x000000f6, 0, 0x7fffffff,
153 0x00000104, 1, 0x000000a1, 0x000000b0, 0x7fffffff,
154 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 };
156 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
157
158 rc->op(*ra, *rb, SkRegion::kUnion_Op);
159}
160#endif
161
reed@android.comf76bacf2009-05-13 14:00:33 +0000162static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
163 dst->fLeft = (int)::roundf(src.fLeft * scale);
164 dst->fTop = (int)::roundf(src.fTop * scale);
165 dst->fRight = (int)::roundf(src.fRight * scale);
166 dst->fBottom = (int)::roundf(src.fBottom * scale);
167}
168
169static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
170 SkRegion tmp;
171 SkRegion::Iterator iter(src);
172
173 for (; !iter.done(); iter.next()) {
174 SkIRect r;
175 scale_rect(&r, iter.rect(), scale);
176 tmp.op(r, SkRegion::kUnion_Op);
177 }
178 dst->swap(tmp);
179}
180
181static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
182 const SkPaint& paint) {
183 SkRegion scaled;
184 scale_rgn(&scaled, rgn, 0.5f);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185
reed@android.comf76bacf2009-05-13 14:00:33 +0000186 SkRegion::Iterator iter(rgn);
187
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 for (; !iter.done(); iter.next())
189 {
190 SkRect r;
191 r.set(iter.rect());
192 canvas->drawRect(r, paint);
193 }
194}
195
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000196class RegionView : public SampleView {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197public:
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000198 RegionView() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fBase.set(100, 100, 150, 150);
200 fRect = fBase;
201 fRect.inset(5, 5);
202 fRect.offset(25, 25);
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000203 this->setBGColor(0xFFDDDDDD);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
205
reed@google.com4b5894c2012-05-02 18:03:32 +0000206 void build_base_rgn(SkRegion* rgn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 rgn->setRect(fBase);
208 SkIRect r = fBase;
209 r.offset(75, 20);
210 rgn->op(r, SkRegion::kUnion_Op);
reed@google.com4b5894c2012-05-02 18:03:32 +0000211 }
212
213 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
214 build_base_rgn(rgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 rgn->op(fRect, op);
216 }
217
218
219protected:
220 // overrides from SkEventSink
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000221 virtual bool onQuery(SkEvent* evt) {
222 if (SampleCode::TitleQ(*evt)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 SampleCode::TitleR(evt, "Regions");
224 return true;
225 }
226 return this->INHERITED::onQuery(evt);
227 }
228
reed@google.com4b5894c2012-05-02 18:03:32 +0000229 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
230 bool hilite) {
231 SkPaint paint;
232 paint.setAntiAlias(true);
233 paint.setTextSize(SkIntToScalar(20));
234 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
235 canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
236 }
237
238 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
239 SkRegion rgn;
240 build_base_rgn(&rgn);
241
242 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
243 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
244 }
245
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000246 void drawOrig(SkCanvas* canvas, bool bg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 SkRect r;
248 SkPaint paint;
249
250 paint.setStyle(SkPaint::kStroke_Style);
251 if (bg)
252 paint.setColor(0xFFBBBBBB);
253
reed@google.com4b5894c2012-05-02 18:03:32 +0000254 SkRegion rgn;
255 build_base_rgn(&rgn);
256 paint_rgn(canvas, rgn, paint);
257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 r.set(fRect);
259 canvas->drawRect(r, paint);
260 }
261
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000262 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 SkRegion rgn;
264
265 this->build_rgn(&rgn, op);
266
267 {
268 SkRegion tmp, tmp2(rgn);
269
270 tmp = tmp2;
271 tmp.translate(5, -3);
272
273 {
274 char buffer[1000];
275 size_t size = tmp.flatten(NULL);
276 SkASSERT(size <= sizeof(buffer));
277 size_t size2 = tmp.flatten(buffer);
278 SkASSERT(size == size2);
279
280 SkRegion tmp3;
281 size2 = tmp3.unflatten(buffer);
282 SkASSERT(size == size2);
283
284 SkASSERT(tmp3 == tmp);
285 }
286
287 rgn.translate(20, 30, &tmp);
288 SkASSERT(rgn.isEmpty() || tmp != rgn);
289 tmp.translate(-20, -30);
290 SkASSERT(tmp == rgn);
291 }
292
293 this->drawOrig(canvas, true);
294
295 SkPaint paint;
296 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
297 paint_rgn(canvas, rgn, paint);
298
299 paint.setStyle(SkPaint::kStroke_Style);
300 paint.setColor(color);
301 paint_rgn(canvas, rgn, paint);
302 }
303
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000304 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 SkRegion rgn;
306 SkPath path;
307
308 this->build_rgn(&rgn, op);
309 rgn.getBoundaryPath(&path);
310
311 this->drawOrig(canvas, true);
312
313 SkPaint paint;
314
315 paint.setStyle(SkPaint::kFill_Style);
316 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
317 canvas->drawPath(path, paint);
318 paint.setColor(color);
319 paint.setStyle(SkPaint::kStroke_Style);
320 canvas->drawPath(path, paint);
321 }
322
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000323 virtual void onDrawContent(SkCanvas* canvas) {
reed@google.com4b5894c2012-05-02 18:03:32 +0000324// test_strokerect(canvas); return;
325// test_text(canvas); return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326#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
reed@google.com4b5894c2012-05-02 18:03:32 +0000346 const SkPoint origins[] = {
347 { 30*SK_Scalar1, 50*SK_Scalar1 },
348 { 150*SK_Scalar1, 50*SK_Scalar1 },
349 };
350 this->drawPredicates(canvas, origins);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351
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
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000375 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 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
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000380 canvas->save();
381 canvas->translate(0, SkIntToScalar(200));
382 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
383 canvas->restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384
385 canvas->translate(SkIntToScalar(200), 0);
386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 }
388
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000389 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
reed@android.com671cd652009-05-22 20:44:12 +0000390 return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 }
392
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000393 virtual bool onClick(Click* click) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
395 click->fICurr.fY - click->fIPrev.fY);
reed@android.com671cd652009-05-22 20:44:12 +0000396 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 return true;
398 }
399
400private:
401 SkIRect fBase, fRect;
402
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000403 typedef SampleView INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404};
405
406//////////////////////////////////////////////////////////////////////////////
407
408static SkView* MyFactory() { return new RegionView; }
409static SkViewRegister reg(MyFactory);
410