blob: eb3e134af9ef7db48514aa80523dcdeb502fdca4 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00007#include "SampleCode.h"
8#include "SkView.h"
9#include "SkCanvas.h"
10#include "SkGradientShader.h"
11#include "SkPath.h"
12#include "SkRegion.h"
13#include "SkShader.h"
14#include "SkUtils.h"
15#include "SkImageDecoder.h"
16
bungemanb7069e92015-07-21 14:14:30 -070017#include <math.h>
18
reed@google.com4b5894c2012-05-02 18:03:32 +000019static void test_strokerect(SkCanvas* canvas) {
20 int width = 100;
21 int height = 100;
22
23 SkBitmap bitmap;
commit-bot@chromium.orga8c18312014-02-17 02:55:57 +000024 bitmap.allocPixels(SkImageInfo::MakeA8(width*2, height*2));
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);
halcanary96fcdcc2015-08-27 07:41:13 -070046 canvas->drawBitmap(bitmap, 0, 0, nullptr);
reed@google.com4b5894c2012-05-02 18:03:32 +000047
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);
halcanary96fcdcc2015-08-27 07:41:13 -070051 canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, nullptr);
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
halcanary96fcdcc2015-08-27 07:41:13 -070068 canvas->saveLayer(&bounds, nullptr);
reed@google.com4b5894c2012-05-02 18:03:32 +000069 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;
halcanary96fcdcc2015-08-27 07:41:13 -0700115 paint.setShader(nullptr);
reed@google.com4b5894c2012-05-02 18:03:32 +0000116 drawFadingText(canvas, str, len, x, y, paint);
117}
118
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119#ifdef SK_DEBUG
120static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
reed@google.com7fa2a652014-01-27 13:42:58 +0000121 int count, int32_t runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 SkIRect r;
123 r.set(left, top, right, bottom);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 rgn->debugSetRuns(runs, count);
126 SkASSERT(rgn->getBounds() == r);
127}
128
129static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
130 static int32_t dataA[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000131 0x00000001,
132 0x000001dd, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
133 0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
134 0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
135 0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
136 0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
137 0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
138 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 };
140 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
141
142 static int32_t dataB[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000143 0x000000b6,
144 0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
145 0x000000d6, 0, 0x7fffffff,
146 0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
147 0x000000e6, 0, 0x7fffffff,
148 0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
149 0x000000f6, 0, 0x7fffffff,
150 0x00000104, 1, 0x000000a1, 0x000000b0, 0x7fffffff,
151 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 };
153 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000154
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 rc->op(*ra, *rb, SkRegion::kUnion_Op);
156}
157#endif
158
reed@android.comf76bacf2009-05-13 14:00:33 +0000159static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
160 dst->fLeft = (int)::roundf(src.fLeft * scale);
161 dst->fTop = (int)::roundf(src.fTop * scale);
162 dst->fRight = (int)::roundf(src.fRight * scale);
163 dst->fBottom = (int)::roundf(src.fBottom * scale);
164}
165
166static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
167 SkRegion tmp;
168 SkRegion::Iterator iter(src);
169
170 for (; !iter.done(); iter.next()) {
171 SkIRect r;
172 scale_rect(&r, iter.rect(), scale);
173 tmp.op(r, SkRegion::kUnion_Op);
174 }
175 dst->swap(tmp);
176}
177
178static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
179 const SkPaint& paint) {
180 SkRegion scaled;
181 scale_rgn(&scaled, rgn, 0.5f);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000182
reed@android.comf76bacf2009-05-13 14:00:33 +0000183 SkRegion::Iterator iter(rgn);
184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 for (; !iter.done(); iter.next())
186 {
187 SkRect r;
188 r.set(iter.rect());
189 canvas->drawRect(r, paint);
190 }
191}
192
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000193class RegionView : public SampleView {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194public:
rmistry@google.comae933ce2012-08-23 18:19:56 +0000195 RegionView() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 fBase.set(100, 100, 150, 150);
197 fRect = fBase;
198 fRect.inset(5, 5);
199 fRect.offset(25, 25);
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000200 this->setBGColor(0xFFDDDDDD);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 }
202
reed@google.com4b5894c2012-05-02 18:03:32 +0000203 void build_base_rgn(SkRegion* rgn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 rgn->setRect(fBase);
205 SkIRect r = fBase;
206 r.offset(75, 20);
207 rgn->op(r, SkRegion::kUnion_Op);
reed@google.com4b5894c2012-05-02 18:03:32 +0000208 }
209
210 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
211 build_base_rgn(rgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 rgn->op(fRect, op);
213 }
214
215
216protected:
217 // overrides from SkEventSink
mtklein36352bf2015-03-25 18:17:31 -0700218 bool onQuery(SkEvent* evt) override {
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000219 if (SampleCode::TitleQ(*evt)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 SampleCode::TitleR(evt, "Regions");
221 return true;
222 }
223 return this->INHERITED::onQuery(evt);
224 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000225
reed@google.com4b5894c2012-05-02 18:03:32 +0000226 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
227 bool hilite) {
228 SkPaint paint;
229 paint.setAntiAlias(true);
230 paint.setTextSize(SkIntToScalar(20));
231 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
232 canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
233 }
234
235 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
236 SkRegion rgn;
237 build_base_rgn(&rgn);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000238
reed@google.com4b5894c2012-05-02 18:03:32 +0000239 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
240 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
241 }
242
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000243 void drawOrig(SkCanvas* canvas, bool bg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 SkRect r;
245 SkPaint paint;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 paint.setStyle(SkPaint::kStroke_Style);
248 if (bg)
249 paint.setColor(0xFFBBBBBB);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000250
reed@google.com4b5894c2012-05-02 18:03:32 +0000251 SkRegion rgn;
252 build_base_rgn(&rgn);
253 paint_rgn(canvas, rgn, paint);
254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 r.set(fRect);
256 canvas->drawRect(r, paint);
257 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000258
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000259 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkRegion rgn;
261
262 this->build_rgn(&rgn, op);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 {
265 SkRegion tmp, tmp2(rgn);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 tmp = tmp2;
268 tmp.translate(5, -3);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 {
271 char buffer[1000];
halcanary96fcdcc2015-08-27 07:41:13 -0700272 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 SkASSERT(size <= sizeof(buffer));
humper@google.com0e515772013-01-07 19:54:40 +0000274 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000276
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 SkRegion tmp3;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000278 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 SkASSERT(tmp3 == tmp);
282 }
283
284 rgn.translate(20, 30, &tmp);
285 SkASSERT(rgn.isEmpty() || tmp != rgn);
286 tmp.translate(-20, -30);
287 SkASSERT(tmp == rgn);
288 }
289
290 this->drawOrig(canvas, true);
291
292 SkPaint paint;
293 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
294 paint_rgn(canvas, rgn, paint);
295
296 paint.setStyle(SkPaint::kStroke_Style);
297 paint.setColor(color);
298 paint_rgn(canvas, rgn, paint);
299 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000300
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000301 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 SkRegion rgn;
303 SkPath path;
304
305 this->build_rgn(&rgn, op);
306 rgn.getBoundaryPath(&path);
307
308 this->drawOrig(canvas, true);
309
310 SkPaint paint;
311
312 paint.setStyle(SkPaint::kFill_Style);
313 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
314 canvas->drawPath(path, paint);
315 paint.setColor(color);
316 paint.setStyle(SkPaint::kStroke_Style);
317 canvas->drawPath(path, paint);
318 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000319
mtklein36352bf2015-03-25 18:17:31 -0700320 void onDrawContent(SkCanvas* canvas) override {
caryclark@google.com02939ce2012-06-06 12:09:51 +0000321 if (false) { // avoid bit rot, suppress warning
322 test_strokerect(canvas);
323 return;
324 }
325 if (false) { // avoid bit rot, suppress warning
326 test_text(canvas);
327 return;
328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329#ifdef SK_DEBUG
330 if (true) {
331 SkRegion a, b, c;
332 test_union_bug_1505668(&a, &b, &c);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000333
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 if (false) { // draw the result of the test
335 SkPaint paint;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
338 paint.setColor(SK_ColorRED);
339 paint_rgn(canvas, a, paint);
340 paint.setColor(0x800000FF);
341 paint_rgn(canvas, b, paint);
342 paint.setColor(SK_ColorBLACK);
343 paint.setStyle(SkPaint::kStroke_Style);
344 // paint_rgn(canvas, c, paint);
345 return;
346 }
347 }
348#endif
reed@google.com4b5894c2012-05-02 18:03:32 +0000349 const SkPoint origins[] = {
350 { 30*SK_Scalar1, 50*SK_Scalar1 },
351 { 150*SK_Scalar1, 50*SK_Scalar1 },
352 };
353 this->drawPredicates(canvas, origins);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354
355 static const struct {
356 SkColor fColor;
357 const char* fName;
358 SkRegion::Op fOp;
359 } gOps[] = {
360 { SK_ColorBLACK, "Difference", SkRegion::kDifference_Op },
361 { SK_ColorRED, "Intersect", SkRegion::kIntersect_Op },
362 { 0xFF008800, "Union", SkRegion::kUnion_Op },
363 { SK_ColorBLUE, "XOR", SkRegion::kXOR_Op }
364 };
365
366 SkPaint textPaint;
367 textPaint.setAntiAlias(true);
368 textPaint.setTextSize(SK_Scalar1*24);
369
370 this->drawOrig(canvas, false);
371 canvas->save();
372 canvas->translate(SkIntToScalar(200), 0);
373 this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
374 canvas->restore();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000375
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 canvas->translate(0, SkIntToScalar(200));
377
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000378 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
380
381 this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
382
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000383 canvas->save();
384 canvas->translate(0, SkIntToScalar(200));
385 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
386 canvas->restore();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000387
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388 canvas->translate(SkIntToScalar(200), 0);
389 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000391
mike@reedtribe.orgdd0c3a52013-02-18 21:52:43 +0000392 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
mtklein36352bf2015-03-25 18:17:31 -0700393 unsigned modi) override {
reed@google.come1ca7052013-12-17 19:22:07 +0000394 return fRect.contains(SkScalarRoundToInt(x),
halcanary96fcdcc2015-08-27 07:41:13 -0700395 SkScalarRoundToInt(y)) ? new Click(this) : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000397
mtklein36352bf2015-03-25 18:17:31 -0700398 bool onClick(Click* click) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
400 click->fICurr.fY - click->fIPrev.fY);
halcanary96fcdcc2015-08-27 07:41:13 -0700401 this->inval(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 return true;
403 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000404
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405private:
406 SkIRect fBase, fRect;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000407
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000408 typedef SampleView INHERITED;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409};
410
411//////////////////////////////////////////////////////////////////////////////
412
413static SkView* MyFactory() { return new RegionView; }
414static SkViewRegister reg(MyFactory);