blob: b4d540ca43b8d470c76c2e1fc4adc6d55124aa8e [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;
robertphillips@google.com4debcac2012-05-14 16:33:36 +000031 path.addRect(0.0f, 0.0f,
32 SkIntToScalar(width), SkIntToScalar(height),
33 SkPath::kCW_Direction);
34 SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
reed@google.com4b5894c2012-05-02 18:03:32 +000035
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(0);
45 c.drawRect(r, paint);
46 canvas->drawBitmap(bitmap, 0, 0, NULL);
47
48 // use the path
49 c.clear(0);
50 c.drawPath(path, paint);
robertphillips@google.com4debcac2012-05-14 16:33:36 +000051 canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, NULL);
reed@google.com4b5894c2012-05-02 18:03:32 +000052}
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, NULL);
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.
robertphillips@google.com4debcac2012-05-14 16:33:36 +000079 const SkScalar pos[] = { 0, SkFloatToScalar(0.9f), SK_Scalar1 };
reed@google.com4b5894c2012-05-02 18:03:32 +000080
81 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 3,
82 SkShader::kClamp_TileMode);
83 SkPaint p;
84 p.setShader(s)->unref();
85 p.setXfermodeMode(SkXfermode::kDstIn_Mode);
86 canvas->drawRect(bounds, p);
87
88 canvas->restore();
89}
90
91static void test_text(SkCanvas* canvas) {
92 SkPaint paint;
93 paint.setAntiAlias(true);
94 paint.setTextSize(20);
95
96 const char* str = "Hamburgefons";
97 size_t len = strlen(str);
98 SkScalar x = 20;
99 SkScalar y = 20;
100
101 canvas->drawText(str, len, x, y, paint);
102
103 y += 20;
104
105 const SkPoint pts[] = { { x, y }, { x + paint.measureText(str, len), y } };
106 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
107 const SkScalar pos[] = { 0, 0.9f, 1 };
108 SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos,
109 SK_ARRAY_COUNT(colors),
110 SkShader::kClamp_TileMode);
111 paint.setShader(s)->unref();
112 canvas->drawText(str, len, x, y, paint);
113
114 y += 20;
115 paint.setShader(NULL);
116 drawFadingText(canvas, str, len, x, y, paint);
117}
118
reed@android.comf2b98d62010-12-20 18:26:13 +0000119#ifdef SK_BUILD_FOR_WIN
120// windows doesn't have roundf
121inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
122#endif
123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124#ifdef SK_DEBUG
125static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
126 size_t count, int32_t runs[]) {
127 SkIRect r;
128 r.set(left, top, right, bottom);
129
130 rgn->debugSetRuns(runs, count);
131 SkASSERT(rgn->getBounds() == r);
132}
133
134static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
135 static int32_t dataA[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000136 0x00000001,
137 0x000001dd, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
138 0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
139 0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
140 0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
141 0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
142 0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
143 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144 };
145 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
146
147 static int32_t dataB[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000148 0x000000b6,
149 0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
150 0x000000d6, 0, 0x7fffffff,
151 0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
152 0x000000e6, 0, 0x7fffffff,
153 0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
154 0x000000f6, 0, 0x7fffffff,
155 0x00000104, 1, 0x000000a1, 0x000000b0, 0x7fffffff,
156 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 };
158 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
159
160 rc->op(*ra, *rb, SkRegion::kUnion_Op);
161}
162#endif
163
reed@android.comf76bacf2009-05-13 14:00:33 +0000164static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
165 dst->fLeft = (int)::roundf(src.fLeft * scale);
166 dst->fTop = (int)::roundf(src.fTop * scale);
167 dst->fRight = (int)::roundf(src.fRight * scale);
168 dst->fBottom = (int)::roundf(src.fBottom * scale);
169}
170
171static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
172 SkRegion tmp;
173 SkRegion::Iterator iter(src);
174
175 for (; !iter.done(); iter.next()) {
176 SkIRect r;
177 scale_rect(&r, iter.rect(), scale);
178 tmp.op(r, SkRegion::kUnion_Op);
179 }
180 dst->swap(tmp);
181}
182
183static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
184 const SkPaint& paint) {
185 SkRegion scaled;
186 scale_rgn(&scaled, rgn, 0.5f);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
reed@android.comf76bacf2009-05-13 14:00:33 +0000188 SkRegion::Iterator iter(rgn);
189
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 for (; !iter.done(); iter.next())
191 {
192 SkRect r;
193 r.set(iter.rect());
194 canvas->drawRect(r, paint);
195 }
196}
197
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000198class RegionView : public SampleView {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199public:
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000200 RegionView() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 fBase.set(100, 100, 150, 150);
202 fRect = fBase;
203 fRect.inset(5, 5);
204 fRect.offset(25, 25);
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000205 this->setBGColor(0xFFDDDDDD);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 }
207
reed@google.com4b5894c2012-05-02 18:03:32 +0000208 void build_base_rgn(SkRegion* rgn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 rgn->setRect(fBase);
210 SkIRect r = fBase;
211 r.offset(75, 20);
212 rgn->op(r, SkRegion::kUnion_Op);
reed@google.com4b5894c2012-05-02 18:03:32 +0000213 }
214
215 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
216 build_base_rgn(rgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 rgn->op(fRect, op);
218 }
219
220
221protected:
222 // overrides from SkEventSink
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000223 virtual bool onQuery(SkEvent* evt) {
224 if (SampleCode::TitleQ(*evt)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 SampleCode::TitleR(evt, "Regions");
226 return true;
227 }
228 return this->INHERITED::onQuery(evt);
229 }
230
reed@google.com4b5894c2012-05-02 18:03:32 +0000231 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
232 bool hilite) {
233 SkPaint paint;
234 paint.setAntiAlias(true);
235 paint.setTextSize(SkIntToScalar(20));
236 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
237 canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
238 }
239
240 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
241 SkRegion rgn;
242 build_base_rgn(&rgn);
243
244 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
245 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
246 }
247
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000248 void drawOrig(SkCanvas* canvas, bool bg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 SkRect r;
250 SkPaint paint;
251
252 paint.setStyle(SkPaint::kStroke_Style);
253 if (bg)
254 paint.setColor(0xFFBBBBBB);
255
reed@google.com4b5894c2012-05-02 18:03:32 +0000256 SkRegion rgn;
257 build_base_rgn(&rgn);
258 paint_rgn(canvas, rgn, paint);
259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 r.set(fRect);
261 canvas->drawRect(r, paint);
262 }
263
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000264 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkRegion rgn;
266
267 this->build_rgn(&rgn, op);
268
269 {
270 SkRegion tmp, tmp2(rgn);
271
272 tmp = tmp2;
273 tmp.translate(5, -3);
274
275 {
276 char buffer[1000];
277 size_t size = tmp.flatten(NULL);
278 SkASSERT(size <= sizeof(buffer));
279 size_t size2 = tmp.flatten(buffer);
280 SkASSERT(size == size2);
281
282 SkRegion tmp3;
283 size2 = tmp3.unflatten(buffer);
284 SkASSERT(size == size2);
285
286 SkASSERT(tmp3 == tmp);
287 }
288
289 rgn.translate(20, 30, &tmp);
290 SkASSERT(rgn.isEmpty() || tmp != rgn);
291 tmp.translate(-20, -30);
292 SkASSERT(tmp == rgn);
293 }
294
295 this->drawOrig(canvas, true);
296
297 SkPaint paint;
298 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
299 paint_rgn(canvas, rgn, paint);
300
301 paint.setStyle(SkPaint::kStroke_Style);
302 paint.setColor(color);
303 paint_rgn(canvas, rgn, paint);
304 }
305
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000306 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 SkRegion rgn;
308 SkPath path;
309
310 this->build_rgn(&rgn, op);
311 rgn.getBoundaryPath(&path);
312
313 this->drawOrig(canvas, true);
314
315 SkPaint paint;
316
317 paint.setStyle(SkPaint::kFill_Style);
318 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
319 canvas->drawPath(path, paint);
320 paint.setColor(color);
321 paint.setStyle(SkPaint::kStroke_Style);
322 canvas->drawPath(path, paint);
323 }
324
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000325 virtual void onDrawContent(SkCanvas* canvas) {
reed@google.com4b5894c2012-05-02 18:03:32 +0000326// test_strokerect(canvas); return;
327// test_text(canvas); return;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328#ifdef SK_DEBUG
329 if (true) {
330 SkRegion a, b, c;
331 test_union_bug_1505668(&a, &b, &c);
332
333 if (false) { // draw the result of the test
334 SkPaint paint;
335
336 canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
337 paint.setColor(SK_ColorRED);
338 paint_rgn(canvas, a, paint);
339 paint.setColor(0x800000FF);
340 paint_rgn(canvas, b, paint);
341 paint.setColor(SK_ColorBLACK);
342 paint.setStyle(SkPaint::kStroke_Style);
343 // paint_rgn(canvas, c, paint);
344 return;
345 }
346 }
347#endif
reed@google.com4b5894c2012-05-02 18:03:32 +0000348 const SkPoint origins[] = {
349 { 30*SK_Scalar1, 50*SK_Scalar1 },
350 { 150*SK_Scalar1, 50*SK_Scalar1 },
351 };
352 this->drawPredicates(canvas, origins);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 static const struct {
355 SkColor fColor;
356 const char* fName;
357 SkRegion::Op fOp;
358 } gOps[] = {
359 { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
360 { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
361 { 0xFF008800, "Union", SkRegion::kUnion_Op },
362 { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
363 };
364
365 SkPaint textPaint;
366 textPaint.setAntiAlias(true);
367 textPaint.setTextSize(SK_Scalar1*24);
368
369 this->drawOrig(canvas, false);
370 canvas->save();
371 canvas->translate(SkIntToScalar(200), 0);
372 this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
373 canvas->restore();
374
375 canvas->translate(0, SkIntToScalar(200));
376
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000377 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
379
380 this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
381
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000382 canvas->save();
383 canvas->translate(0, SkIntToScalar(200));
384 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
385 canvas->restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386
387 canvas->translate(SkIntToScalar(200), 0);
388 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389 }
390
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000391 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
reed@android.com671cd652009-05-22 20:44:12 +0000392 return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 }
394
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000395 virtual bool onClick(Click* click) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
397 click->fICurr.fY - click->fIPrev.fY);
reed@android.com671cd652009-05-22 20:44:12 +0000398 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 return true;
400 }
401
402private:
403 SkIRect fBase, fRect;
404
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000405 typedef SampleView INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406};
407
408//////////////////////////////////////////////////////////////////////////////
409
410static SkView* MyFactory() { return new RegionView; }
411static SkViewRegister reg(MyFactory);
412