blob: 1fee2fa7f9a65db8418426b9f7f6d1f5b2c00ac5 [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 */
reed8a21c9f2016-03-08 18:50:00 -08007
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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
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
reed@google.com4b5894c2012-05-02 18:03:32 +000081 SkPaint p;
reed8a21c9f2016-03-08 18:50:00 -080082 p.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 3, SkShader::kClamp_TileMode));
reed374772b2016-10-05 17:33:02 -070083 p.setBlendMode(SkBlendMode::kDstIn);
reed@google.com4b5894c2012-05-02 18:03:32 +000084 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);
rmistry@google.comae933ce2012-08-23 18:19:56 +000093
reed@google.com4b5894c2012-05-02 18:03:32 +000094 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 };
reed8a21c9f2016-03-08 18:50:00 -0800106 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos,
reed@google.com4b5894c2012-05-02 18:03:32 +0000107 SK_ARRAY_COUNT(colors),
reed8a21c9f2016-03-08 18:50:00 -0800108 SkShader::kClamp_TileMode));
reed@google.com4b5894c2012-05-02 18:03:32 +0000109 canvas->drawText(str, len, x, y, paint);
110
111 y += 20;
halcanary96fcdcc2015-08-27 07:41:13 -0700112 paint.setShader(nullptr);
reed@google.com4b5894c2012-05-02 18:03:32 +0000113 drawFadingText(canvas, str, len, x, y, paint);
114}
115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116#ifdef SK_DEBUG
117static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
reed@google.com7fa2a652014-01-27 13:42:58 +0000118 int count, int32_t runs[]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkIRect r;
120 r.set(left, top, right, bottom);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 rgn->debugSetRuns(runs, count);
123 SkASSERT(rgn->getBounds() == r);
124}
125
126static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
127 static int32_t dataA[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000128 0x00000001,
129 0x000001dd, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
130 0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
131 0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
132 0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
133 0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
134 0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
135 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 };
137 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
138
139 static int32_t dataB[] = {
reed@google.com4b5894c2012-05-02 18:03:32 +0000140 0x000000b6,
141 0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
142 0x000000d6, 0, 0x7fffffff,
143 0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
144 0x000000e6, 0, 0x7fffffff,
145 0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
146 0x000000f6, 0, 0x7fffffff,
147 0x00000104, 1, 0x000000a1, 0x000000b0, 0x7fffffff,
148 0x7fffffff
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 };
150 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000151
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 rc->op(*ra, *rb, SkRegion::kUnion_Op);
153}
154#endif
155
reed@android.comf76bacf2009-05-13 14:00:33 +0000156static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
157 dst->fLeft = (int)::roundf(src.fLeft * scale);
158 dst->fTop = (int)::roundf(src.fTop * scale);
159 dst->fRight = (int)::roundf(src.fRight * scale);
160 dst->fBottom = (int)::roundf(src.fBottom * scale);
161}
162
163static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
164 SkRegion tmp;
165 SkRegion::Iterator iter(src);
166
167 for (; !iter.done(); iter.next()) {
168 SkIRect r;
169 scale_rect(&r, iter.rect(), scale);
170 tmp.op(r, SkRegion::kUnion_Op);
171 }
172 dst->swap(tmp);
173}
174
175static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
176 const SkPaint& paint) {
177 SkRegion scaled;
178 scale_rgn(&scaled, rgn, 0.5f);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000179
reed@android.comf76bacf2009-05-13 14:00:33 +0000180 SkRegion::Iterator iter(rgn);
181
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 for (; !iter.done(); iter.next())
183 {
184 SkRect r;
185 r.set(iter.rect());
186 canvas->drawRect(r, paint);
187 }
188}
189
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000190class RegionView : public SampleView {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191public:
rmistry@google.comae933ce2012-08-23 18:19:56 +0000192 RegionView() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fBase.set(100, 100, 150, 150);
194 fRect = fBase;
195 fRect.inset(5, 5);
196 fRect.offset(25, 25);
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000197 this->setBGColor(0xFFDDDDDD);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 }
199
reed@google.com4b5894c2012-05-02 18:03:32 +0000200 void build_base_rgn(SkRegion* rgn) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 rgn->setRect(fBase);
202 SkIRect r = fBase;
203 r.offset(75, 20);
204 rgn->op(r, SkRegion::kUnion_Op);
reed@google.com4b5894c2012-05-02 18:03:32 +0000205 }
206
207 void build_rgn(SkRegion* rgn, SkRegion::Op op) {
208 build_base_rgn(rgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 rgn->op(fRect, op);
210 }
211
212
213protected:
214 // overrides from SkEventSink
mtklein36352bf2015-03-25 18:17:31 -0700215 bool onQuery(SkEvent* evt) override {
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000216 if (SampleCode::TitleQ(*evt)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 SampleCode::TitleR(evt, "Regions");
218 return true;
219 }
220 return this->INHERITED::onQuery(evt);
221 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000222
reed@google.com4b5894c2012-05-02 18:03:32 +0000223 static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
224 bool hilite) {
225 SkPaint paint;
226 paint.setAntiAlias(true);
227 paint.setTextSize(SkIntToScalar(20));
228 paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
Cary Clark2a475ea2017-04-28 15:35:12 -0400229 canvas->drawString(text, loc.fX, loc.fY, paint);
reed@google.com4b5894c2012-05-02 18:03:32 +0000230 }
231
232 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
233 SkRegion rgn;
234 build_base_rgn(&rgn);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000235
reed@google.com4b5894c2012-05-02 18:03:32 +0000236 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
237 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
238 }
239
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000240 void drawOrig(SkCanvas* canvas, bool bg) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 SkRect r;
242 SkPaint paint;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 paint.setStyle(SkPaint::kStroke_Style);
245 if (bg)
246 paint.setColor(0xFFBBBBBB);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000247
reed@google.com4b5894c2012-05-02 18:03:32 +0000248 SkRegion rgn;
249 build_base_rgn(&rgn);
250 paint_rgn(canvas, rgn, paint);
251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 r.set(fRect);
253 canvas->drawRect(r, paint);
254 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000255
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000256 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkRegion rgn;
258
259 this->build_rgn(&rgn, op);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000260
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 {
262 SkRegion tmp, tmp2(rgn);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 tmp = tmp2;
265 tmp.translate(5, -3);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 {
268 char buffer[1000];
halcanary96fcdcc2015-08-27 07:41:13 -0700269 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkASSERT(size <= sizeof(buffer));
humper@google.com0e515772013-01-07 19:54:40 +0000271 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 SkRegion tmp3;
commit-bot@chromium.org4faa8692013-11-05 15:46:56 +0000275 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 SkASSERT(size == size2);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000277
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 SkASSERT(tmp3 == tmp);
279 }
280
281 rgn.translate(20, 30, &tmp);
282 SkASSERT(rgn.isEmpty() || tmp != rgn);
283 tmp.translate(-20, -30);
284 SkASSERT(tmp == rgn);
285 }
286
287 this->drawOrig(canvas, true);
288
289 SkPaint paint;
290 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
291 paint_rgn(canvas, rgn, paint);
292
293 paint.setStyle(SkPaint::kStroke_Style);
294 paint.setColor(color);
295 paint_rgn(canvas, rgn, paint);
296 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000297
mike@reedtribe.org5fd92432011-05-05 01:59:48 +0000298 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 SkRegion rgn;
300 SkPath path;
301
302 this->build_rgn(&rgn, op);
303 rgn.getBoundaryPath(&path);
304
305 this->drawOrig(canvas, true);
306
307 SkPaint paint;
308
309 paint.setStyle(SkPaint::kFill_Style);
310 paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
311 canvas->drawPath(path, paint);
312 paint.setColor(color);
313 paint.setStyle(SkPaint::kStroke_Style);
314 canvas->drawPath(path, paint);
315 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000316
mtklein36352bf2015-03-25 18:17:31 -0700317 void onDrawContent(SkCanvas* canvas) override {
caryclark@google.com02939ce2012-06-06 12:09:51 +0000318 if (false) { // avoid bit rot, suppress warning
319 test_strokerect(canvas);
320 return;
321 }
322 if (false) { // avoid bit rot, suppress warning
323 test_text(canvas);
324 return;
325 }
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);
rmistry@google.comae933ce2012-08-23 18:19:56 +0000330
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 if (false) { // draw the result of the test
332 SkPaint paint;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000333
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334 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();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 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++) {
Cary Clark2a475ea2017-04-28 15:35:12 -0400376 canvas->drawString(gOps[op].fName, SkIntToScalar(75), SkIntToScalar(50), textPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377
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();
rmistry@google.comae933ce2012-08-23 18:19:56 +0000384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 canvas->translate(SkIntToScalar(200), 0);
386 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000388
mike@reedtribe.orgdd0c3a52013-02-18 21:52:43 +0000389 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
mtklein36352bf2015-03-25 18:17:31 -0700390 unsigned modi) override {
reed@google.come1ca7052013-12-17 19:22:07 +0000391 return fRect.contains(SkScalarRoundToInt(x),
halcanary96fcdcc2015-08-27 07:41:13 -0700392 SkScalarRoundToInt(y)) ? new Click(this) : nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000394
mtklein36352bf2015-03-25 18:17:31 -0700395 bool onClick(Click* click) override {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 fRect.offset(click->fICurr.fX - click->fIPrev.fX,
397 click->fICurr.fY - click->fIPrev.fY);
halcanary96fcdcc2015-08-27 07:41:13 -0700398 this->inval(nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 return true;
400 }
rmistry@google.comae933ce2012-08-23 18:19:56 +0000401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402private:
403 SkIRect fBase, fRect;
rmistry@google.comae933ce2012-08-23 18:19:56 +0000404
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);