1/* 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 */ 7 8#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 17#include <math.h> 18 19static void test_strokerect(SkCanvas* canvas) { 20 int width = 100; 21 int height = 100; 22 23 SkBitmap bitmap; 24 bitmap.allocPixels(SkImageInfo::MakeA8(width*2, height*2)); 25 bitmap.eraseColor(SK_ColorTRANSPARENT); 26 27 SkScalar dx = 20; 28 SkScalar dy = 20; 29 30 SkPath path; 31 path.addRect(0.0f, 0.0f, 32 SkIntToScalar(width), SkIntToScalar(height), 33 SkPath::kCW_Direction); 34 SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)); 35 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(SK_ColorTRANSPARENT); 45 c.drawRect(r, paint); 46 canvas->drawBitmap(bitmap, 0, 0, nullptr); 47 48 // use the path 49 c.clear(SK_ColorTRANSPARENT); 50 c.drawPath(path, paint); 51 canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, nullptr); 52} 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, nullptr); 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. 79 const SkScalar pos[] = { 0, 0.9f, SK_Scalar1 }; 80 81 SkPaint p; 82 p.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 3, SkShader::kClamp_TileMode)); 83 p.setBlendMode(SkBlendMode::kDstIn); 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 paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 107 SK_ARRAY_COUNT(colors), 108 SkShader::kClamp_TileMode)); 109 canvas->drawText(str, len, x, y, paint); 110 111 y += 20; 112 paint.setShader(nullptr); 113 drawFadingText(canvas, str, len, x, y, paint); 114} 115 116#ifdef SK_DEBUG 117static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom, 118 int count, int32_t runs[]) { 119 SkIRect r; 120 r.set(left, top, right, bottom); 121 122 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[] = { 128 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 136 }; 137 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA); 138 139 static int32_t dataB[] = { 140 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 149 }; 150 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB); 151 152 rc->op(*ra, *rb, SkRegion::kUnion_Op); 153} 154#endif 155 156static 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); 179 180 SkRegion::Iterator iter(rgn); 181 182 for (; !iter.done(); iter.next()) 183 { 184 SkRect r; 185 r.set(iter.rect()); 186 canvas->drawRect(r, paint); 187 } 188} 189 190class RegionView : public SampleView { 191public: 192 RegionView() { 193 fBase.set(100, 100, 150, 150); 194 fRect = fBase; 195 fRect.inset(5, 5); 196 fRect.offset(25, 25); 197 this->setBGColor(0xFFDDDDDD); 198 } 199 200 void build_base_rgn(SkRegion* rgn) { 201 rgn->setRect(fBase); 202 SkIRect r = fBase; 203 r.offset(75, 20); 204 rgn->op(r, SkRegion::kUnion_Op); 205 } 206 207 void build_rgn(SkRegion* rgn, SkRegion::Op op) { 208 build_base_rgn(rgn); 209 rgn->op(fRect, op); 210 } 211 212 213protected: 214 // overrides from SkEventSink 215 bool onQuery(SkEvent* evt) override { 216 if (SampleCode::TitleQ(*evt)) { 217 SampleCode::TitleR(evt, "Regions"); 218 return true; 219 } 220 return this->INHERITED::onQuery(evt); 221 } 222 223 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); 229 canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint); 230 } 231 232 void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) { 233 SkRegion rgn; 234 build_base_rgn(&rgn); 235 236 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect)); 237 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect)); 238 } 239 240 void drawOrig(SkCanvas* canvas, bool bg) { 241 SkRect r; 242 SkPaint paint; 243 244 paint.setStyle(SkPaint::kStroke_Style); 245 if (bg) 246 paint.setColor(0xFFBBBBBB); 247 248 SkRegion rgn; 249 build_base_rgn(&rgn); 250 paint_rgn(canvas, rgn, paint); 251 252 r.set(fRect); 253 canvas->drawRect(r, paint); 254 } 255 256 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { 257 SkRegion rgn; 258 259 this->build_rgn(&rgn, op); 260 261 { 262 SkRegion tmp, tmp2(rgn); 263 264 tmp = tmp2; 265 tmp.translate(5, -3); 266 267 { 268 char buffer[1000]; 269 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr); 270 SkASSERT(size <= sizeof(buffer)); 271 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer); 272 SkASSERT(size == size2); 273 274 SkRegion tmp3; 275 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000); 276 SkASSERT(size == size2); 277 278 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 } 297 298 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { 299 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 } 316 317 void onDrawContent(SkCanvas* canvas) override { 318 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 } 326#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 346 const SkPoint origins[] = { 347 { 30*SK_Scalar1, 50*SK_Scalar1 }, 348 { 150*SK_Scalar1, 50*SK_Scalar1 }, 349 }; 350 this->drawPredicates(canvas, origins); 351 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 375 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) { 376 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 380 canvas->save(); 381 canvas->translate(0, SkIntToScalar(200)); 382 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor); 383 canvas->restore(); 384 385 canvas->translate(SkIntToScalar(200), 0); 386 } 387 } 388 389 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, 390 unsigned modi) override { 391 return fRect.contains(SkScalarRoundToInt(x), 392 SkScalarRoundToInt(y)) ? new Click(this) : nullptr; 393 } 394 395 bool onClick(Click* click) override { 396 fRect.offset(click->fICurr.fX - click->fIPrev.fX, 397 click->fICurr.fY - click->fIPrev.fY); 398 this->inval(nullptr); 399 return true; 400 } 401 402private: 403 SkIRect fBase, fRect; 404 405 typedef SampleView INHERITED; 406}; 407 408////////////////////////////////////////////////////////////////////////////// 409 410static SkView* MyFactory() { return new RegionView; } 411static SkViewRegister reg(MyFactory); 412