blob: 886eb96c786630ca6d65ef47b761eee3efefd49c [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();
junov@google.comdbfac8a2012-12-06 21:47:40 +000025 bitmap.eraseColor(SK_ColorTRANSPARENT);
reed@google.com4b5894c2012-05-02 18:03:32 +000026
27 SkScalar dx = 20;
28 SkScalar dy = 20;
rmistry@google.comae933ce2012-08-23 18:19:56 +000029
reed@google.com4b5894c2012-05-02 18:03:32 +000030 SkPath path;
rmistry@google.comae933ce2012-08-23 18:19:56 +000031 path.addRect(0.0f, 0.0f,
32 SkIntToScalar(width), SkIntToScalar(height),
robertphillips@google.com4debcac2012-05-14 16:33:36 +000033 SkPath::kCW_Direction);
34 SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
rmistry@google.comae933ce2012-08-23 18:19:56 +000035
reed@google.com4b5894c2012-05-02 18:03:32 +000036 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
junov@google.comdbfac8a2012-12-06 21:47:40 +000044 c.clear(SK_ColorTRANSPARENT);
reed@google.com4b5894c2012-05-02 18:03:32 +000045 c.drawRect(r, paint);
46 canvas->drawBitmap(bitmap, 0, 0, NULL);
47
48 // use the path
junov@google.comdbfac8a2012-12-06 21:47:40 +000049 c.clear(SK_ColorTRANSPARENT);
reed@google.com4b5894c2012-05-02 18:03:32 +000050 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;
rmistry@google.comae933ce2012-08-23 18:19:56 +000060
reed@google.com4b5894c2012-05-02 18:03:32 +000061 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.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000079 const SkScalar pos[] = { 0, 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);
rmistry@google.comae933ce2012-08-23 18:19:56 +000095
reed@google.com4b5894c2012-05-02 18:03:32 +000096 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);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 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);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 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);
rmistry@google.comae933ce2012-08-23 18:19:56 +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:
rmistry@google.comae933ce2012-08-23 18:19:56 +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 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000230
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);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000243
reed@google.com4b5894c2012-05-02 18:03:32 +0000244 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;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 paint.setStyle(SkPaint::kStroke_Style);
253 if (bg)
254 paint.setColor(0xFFBBBBBB);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000255
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 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000263
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);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 {
270 SkRegion tmp, tmp2(rgn);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 tmp = tmp2;
273 tmp.translate(5, -3);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 {
276 char buffer[1000];
humper@google.com0e515772013-01-07 19:54:40 +0000277 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 SkASSERT(size <= sizeof(buffer));
humper@google.com0e515772013-01-07 19:54:40 +0000279 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 SkRegion tmp3;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000283 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000285
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 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 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000305
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 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000324
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000325 virtual void onDrawContent(SkCanvas* canvas) {
caryclark@google.com02939ce2012-06-06 12:09:51 +0000326 if (false) { // avoid bit rot, suppress warning
327 test_strokerect(canvas);
328 return;
329 }
330 if (false) { // avoid bit rot, suppress warning
331 test_text(canvas);
332 return;
333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334#ifdef SK_DEBUG
335 if (true) {
336 SkRegion a, b, c;
337 test_union_bug_1505668(&a, &b, &c);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 if (false) { // draw the result of the test
340 SkPaint paint;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
343 paint.setColor(SK_ColorRED);
344 paint_rgn(canvas, a, paint);
345 paint.setColor(0x800000FF);
346 paint_rgn(canvas, b, paint);
347 paint.setColor(SK_ColorBLACK);
348 paint.setStyle(SkPaint::kStroke_Style);
349 // paint_rgn(canvas, c, paint);
350 return;
351 }
352 }
353#endif
reed@google.com4b5894c2012-05-02 18:03:32 +0000354 const SkPoint origins[] = {
355 { 30*SK_Scalar1, 50*SK_Scalar1 },
356 { 150*SK_Scalar1, 50*SK_Scalar1 },
357 };
358 this->drawPredicates(canvas, origins);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359
360 static const struct {
361 SkColor fColor;
362 const char* fName;
363 SkRegion::Op fOp;
364 } gOps[] = {
365 { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
366 { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
367 { 0xFF008800, "Union", SkRegion::kUnion_Op },
368 { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
369 };
370
371 SkPaint textPaint;
372 textPaint.setAntiAlias(true);
373 textPaint.setTextSize(SK_Scalar1*24);
374
375 this->drawOrig(canvas, false);
376 canvas->save();
377 canvas->translate(SkIntToScalar(200), 0);
378 this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
379 canvas->restore();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 canvas->translate(0, SkIntToScalar(200));
382
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000383 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
385
386 this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
387
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000388 canvas->save();
389 canvas->translate(0, SkIntToScalar(200));
390 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
391 canvas->restore();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 canvas->translate(SkIntToScalar(200), 0);
394 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000396
mike@reedtribe.orgdd0c3a52013-02-18 21:52:43 +0000397 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
398 unsigned modi) SK_OVERRIDE {
reed@android.com671cd652009-05-22 20:44:12 +0000399 return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000401
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000402 virtual bool onClick(Click* click) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
404 click->fICurr.fY - click->fIPrev.fY);
reed@android.com671cd652009-05-22 20:44:12 +0000405 this->inval(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 return true;
407 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409private:
410 SkIRect fBase, fRect;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000411
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000412 typedef SampleView INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000413};
414
415//////////////////////////////////////////////////////////////////////////////
416
417static SkView* MyFactory() { return new RegionView; }
418static SkViewRegister reg(MyFactory);