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 #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 17 #include <math.h> 18 19 static 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 54 static 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 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 91 static 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(nullptr); 116 drawFadingText(canvas, str, len, x, y, paint); 117 } 118 119 #ifdef SK_DEBUG 120 static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom, 121 int count, int32_t runs[]) { 122 SkIRect r; 123 r.set(left, top, right, bottom); 124 125 rgn->debugSetRuns(runs, count); 126 SkASSERT(rgn->getBounds() == r); 127 } 128 129 static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) { 130 static int32_t dataA[] = { 131 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 139 }; 140 make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA); 141 142 static int32_t dataB[] = { 143 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 152 }; 153 make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB); 154 155 rc->op(*ra, *rb, SkRegion::kUnion_Op); 156 } 157 #endif 158 159 static 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 166 static 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 178 static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn, 179 const SkPaint& paint) { 180 SkRegion scaled; 181 scale_rgn(&scaled, rgn, 0.5f); 182 183 SkRegion::Iterator iter(rgn); 184 185 for (; !iter.done(); iter.next()) 186 { 187 SkRect r; 188 r.set(iter.rect()); 189 canvas->drawRect(r, paint); 190 } 191 } 192 193 class RegionView : public SampleView { 194 public: 195 RegionView() { 196 fBase.set(100, 100, 150, 150); 197 fRect = fBase; 198 fRect.inset(5, 5); 199 fRect.offset(25, 25); 200 this->setBGColor(0xFFDDDDDD); 201 } 202 203 void build_base_rgn(SkRegion* rgn) { 204 rgn->setRect(fBase); 205 SkIRect r = fBase; 206 r.offset(75, 20); 207 rgn->op(r, SkRegion::kUnion_Op); 208 } 209 210 void build_rgn(SkRegion* rgn, SkRegion::Op op) { 211 build_base_rgn(rgn); 212 rgn->op(fRect, op); 213 } 214 215 216 protected: 217 // overrides from SkEventSink 218 bool onQuery(SkEvent* evt) override { 219 if (SampleCode::TitleQ(*evt)) { 220 SampleCode::TitleR(evt, "Regions"); 221 return true; 222 } 223 return this->INHERITED::onQuery(evt); 224 } 225 226 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); 238 239 drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect)); 240 drawstr(canvas, "Contains", pts[1], rgn.contains(fRect)); 241 } 242 243 void drawOrig(SkCanvas* canvas, bool bg) { 244 SkRect r; 245 SkPaint paint; 246 247 paint.setStyle(SkPaint::kStroke_Style); 248 if (bg) 249 paint.setColor(0xFFBBBBBB); 250 251 SkRegion rgn; 252 build_base_rgn(&rgn); 253 paint_rgn(canvas, rgn, paint); 254 255 r.set(fRect); 256 canvas->drawRect(r, paint); 257 } 258 259 void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { 260 SkRegion rgn; 261 262 this->build_rgn(&rgn, op); 263 264 { 265 SkRegion tmp, tmp2(rgn); 266 267 tmp = tmp2; 268 tmp.translate(5, -3); 269 270 { 271 char buffer[1000]; 272 SkDEBUGCODE(size_t size = ) tmp.writeToMemory(nullptr); 273 SkASSERT(size <= sizeof(buffer)); 274 SkDEBUGCODE(size_t size2 = ) tmp.writeToMemory(buffer); 275 SkASSERT(size == size2); 276 277 SkRegion tmp3; 278 SkDEBUGCODE(size2 = ) tmp3.readFromMemory(buffer, 1000); 279 SkASSERT(size == size2); 280 281 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 } 300 301 void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) { 302 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 } 319 320 void onDrawContent(SkCanvas* canvas) override { 321 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 } 329 #ifdef SK_DEBUG 330 if (true) { 331 SkRegion a, b, c; 332 test_union_bug_1505668(&a, &b, &c); 333 334 if (false) { // draw the result of the test 335 SkPaint paint; 336 337 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 349 const SkPoint origins[] = { 350 { 30*SK_Scalar1, 50*SK_Scalar1 }, 351 { 150*SK_Scalar1, 50*SK_Scalar1 }, 352 }; 353 this->drawPredicates(canvas, origins); 354 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(); 375 376 canvas->translate(0, SkIntToScalar(200)); 377 378 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) { 379 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 383 canvas->save(); 384 canvas->translate(0, SkIntToScalar(200)); 385 this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor); 386 canvas->restore(); 387 388 canvas->translate(SkIntToScalar(200), 0); 389 } 390 } 391 392 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, 393 unsigned modi) override { 394 return fRect.contains(SkScalarRoundToInt(x), 395 SkScalarRoundToInt(y)) ? new Click(this) : nullptr; 396 } 397 398 bool onClick(Click* click) override { 399 fRect.offset(click->fICurr.fX - click->fIPrev.fX, 400 click->fICurr.fY - click->fIPrev.fY); 401 this->inval(nullptr); 402 return true; 403 } 404 405 private: 406 SkIRect fBase, fRect; 407 408 typedef SampleView INHERITED; 409 }; 410 411 ////////////////////////////////////////////////////////////////////////////// 412 413 static SkView* MyFactory() { return new RegionView; } 414 static SkViewRegister reg(MyFactory); 415