1 /* 2 * Copyright 2012 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 "sk_tool_utils.h" 9 #include "SampleCode.h" 10 #include "SkView.h" 11 #include "SkCanvas.h" 12 #include "SkPath.h" 13 #include "SkRegion.h" 14 #include "SkShader.h" 15 #include "SkUtils.h" 16 #include "SkImage.h" 17 #include "SkSurface.h" 18 19 #define FAT_PIXEL_COLOR SK_ColorBLACK 20 #define PIXEL_CENTER_SIZE 3 21 #define WIRE_FRAME_COLOR 0xFFFF0000 /*0xFF00FFFF*/ 22 #define WIRE_FRAME_SIZE 1.5f 23 24 static SkScalar apply_grid(SkScalar x) { 25 const SkScalar grid = 2; 26 return SkScalarRoundToScalar(x * grid) / grid; 27 } 28 29 static void apply_grid(SkPoint pts[], int count) { 30 for (int i = 0; i < count; ++i) { 31 pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY)); 32 } 33 } 34 35 static void erase(SkSurface* surface) { 36 surface->getCanvas()->clear(SK_ColorTRANSPARENT); 37 } 38 39 class FatBits { 40 public: 41 FatBits() { 42 fAA = false; 43 fStyle = kHair_Style; 44 fGrid = false; 45 fShowSkeleton = true; 46 fUseGPU = false; 47 fUseClip = false; 48 fRectAsOval = false; 49 fUseTriangle = false; 50 fStrokeCap = SkPaint::kButt_Cap; 51 52 fClipRect.set(2, 2, 11, 8 ); 53 } 54 55 int getZoom() const { return fZoom; } 56 57 bool getAA() const { return fAA; } 58 void setAA(bool aa) { fAA = aa; } 59 60 bool getGrid() const { return fGrid; } 61 void setGrid(bool g) { fGrid = g; } 62 63 bool getShowSkeleton() const { return fShowSkeleton; } 64 void setShowSkeleton(bool ss) { fShowSkeleton = ss; } 65 66 bool getUseGPU() const { return fUseGPU; } 67 void setUseGPU(bool ug) { fUseGPU = ug; } 68 69 bool getTriangle() const { return fUseTriangle; } 70 void setTriangle(bool ut) { fUseTriangle = ut; } 71 72 void toggleRectAsOval() { fRectAsOval = !fRectAsOval; } 73 74 bool getUseClip() const { return fUseClip; } 75 void setUseClip(bool uc) { fUseClip = uc; } 76 77 enum Style { 78 kHair_Style, 79 kStroke_Style, 80 }; 81 Style getStyle() const { return fStyle; } 82 void setStyle(Style s) { fStyle = s; } 83 84 void setWHZ(int width, int height, int zoom) { 85 fW = width; 86 fH = height; 87 fZoom = zoom; 88 fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom)); 89 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom)); 90 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom); 91 fShader.reset(sk_tool_utils::create_checkerboard_shader( 92 0xFFCCCCCC, 0xFFFFFFFF, zoom)); 93 94 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 95 fMinSurface.reset(SkSurface::NewRaster(info)); 96 info = info.makeWH(width * zoom, height * zoom); 97 fMaxSurface.reset(SkSurface::NewRaster(info)); 98 } 99 100 void drawBG(SkCanvas*); 101 void drawFG(SkCanvas*); 102 void drawLine(SkCanvas*, SkPoint pts[2]); 103 void drawRect(SkCanvas* canvas, SkPoint pts[2]); 104 void drawTriangle(SkCanvas* canvas, SkPoint pts[3]); 105 106 SkPaint::Cap fStrokeCap; 107 108 private: 109 bool fAA, fGrid, fShowSkeleton, fUseGPU, fUseClip, fRectAsOval, fUseTriangle; 110 Style fStyle; 111 int fW, fH, fZoom; 112 SkMatrix fMatrix, fInverse; 113 SkRect fBounds, fClipRect; 114 SkAutoTUnref<SkShader> fShader; 115 SkAutoTUnref<SkSurface> fMinSurface; 116 SkAutoTUnref<SkSurface> fMaxSurface; 117 118 void setupPaint(SkPaint* paint) { 119 bool aa = this->getAA(); 120 paint->setStrokeCap(fStrokeCap); 121 switch (fStyle) { 122 case kHair_Style: 123 paint->setStrokeWidth(0); 124 break; 125 case kStroke_Style: 126 paint->setStrokeWidth(SK_Scalar1); 127 break; 128 } 129 paint->setAntiAlias(aa); 130 } 131 132 void setupSkeletonPaint(SkPaint* paint) { 133 paint->setStyle(SkPaint::kStroke_Style); 134 paint->setStrokeWidth(WIRE_FRAME_SIZE); 135 paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0); 136 paint->setAntiAlias(true); 137 } 138 139 void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]); 140 void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]); 141 void drawRectSkeleton(SkCanvas* max, const SkRect& r) { 142 SkPaint paint; 143 this->setupSkeletonPaint(&paint); 144 SkPath path; 145 146 if (fUseGPU && fAA) { 147 SkRect rr = r; 148 rr.inset(SkIntToScalar(fZoom)/2, SkIntToScalar(fZoom)/2); 149 path.addRect(rr); 150 path.moveTo(rr.fLeft, rr.fTop); 151 path.lineTo(rr.fRight, rr.fBottom); 152 rr = r; 153 rr.inset(-SkIntToScalar(fZoom)/2, -SkIntToScalar(fZoom)/2); 154 path.addRect(rr); 155 } else { 156 fRectAsOval ? path.addOval(r) : path.addRect(r); 157 if (fUseGPU) { 158 path.moveTo(r.fLeft, r.fTop); 159 path.lineTo(r.fRight, r.fBottom); 160 } 161 } 162 max->drawPath(path, paint); 163 } 164 165 void copyMinToMax() { 166 erase(fMaxSurface); 167 SkCanvas* canvas = fMaxSurface->getCanvas(); 168 canvas->save(); 169 canvas->concat(fMatrix); 170 fMinSurface->draw(canvas, 0, 0, nullptr); 171 canvas->restore(); 172 173 SkPaint paint; 174 paint.setXfermodeMode(SkXfermode::kClear_Mode); 175 for (int iy = 1; iy < fH; ++iy) { 176 SkScalar y = SkIntToScalar(iy * fZoom); 177 canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint); 178 } 179 for (int ix = 1; ix < fW; ++ix) { 180 SkScalar x = SkIntToScalar(ix * fZoom); 181 canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint); 182 } 183 } 184 }; 185 186 void FatBits::drawBG(SkCanvas* canvas) { 187 SkPaint paint; 188 189 paint.setShader(fShader); 190 canvas->drawRect(fBounds, paint); 191 paint.setShader(nullptr); 192 } 193 194 void FatBits::drawFG(SkCanvas* canvas) { 195 SkPaint inner, outer; 196 197 inner.setAntiAlias(true); 198 inner.setColor(SK_ColorBLACK); 199 inner.setStrokeWidth(PIXEL_CENTER_SIZE); 200 201 outer.setAntiAlias(true); 202 outer.setColor(SK_ColorWHITE); 203 outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2); 204 205 SkScalar half = SkIntToScalar(fZoom) / 2; 206 for (int iy = 0; iy < fH; ++iy) { 207 SkScalar y = SkIntToScalar(iy * fZoom) + half; 208 for (int ix = 0; ix < fW; ++ix) { 209 SkScalar x = SkIntToScalar(ix * fZoom) + half; 210 211 canvas->drawPoint(x, y, outer); 212 canvas->drawPoint(x, y, inner); 213 } 214 } 215 216 if (fUseClip) { 217 SkPaint p; 218 p.setStyle(SkPaint::kStroke_Style); 219 p.setColor(SK_ColorLTGRAY); 220 SkRect r = { 221 fClipRect.fLeft * fZoom, 222 fClipRect.fTop * fZoom, 223 fClipRect.fRight * fZoom, 224 fClipRect.fBottom * fZoom 225 }; 226 canvas->drawRect(r, p); 227 } 228 } 229 230 void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) { 231 SkPaint paint; 232 this->setupSkeletonPaint(&paint); 233 234 SkPath path; 235 path.moveTo(pts[0]); 236 path.lineTo(pts[1]); 237 238 switch (fStyle) { 239 case kHair_Style: 240 if (fUseGPU) { 241 SkPaint p; 242 p.setStyle(SkPaint::kStroke_Style); 243 p.setStrokeWidth(SK_Scalar1 * fZoom); 244 SkPath dst; 245 p.getFillPath(path, &dst); 246 path.addPath(dst); 247 } 248 break; 249 case kStroke_Style: { 250 SkPaint p; 251 p.setStyle(SkPaint::kStroke_Style); 252 p.setStrokeWidth(SK_Scalar1 * fZoom); 253 p.setStrokeCap(fStrokeCap); 254 SkPath dst; 255 p.getFillPath(path, &dst); 256 path = dst; 257 258 path.moveTo(pts[0]); 259 path.lineTo(pts[1]); 260 261 if (fUseGPU) { 262 path.moveTo(dst.getPoint(0)); 263 path.lineTo(dst.getPoint(2)); 264 } 265 } break; 266 } 267 max->drawPath(path, paint); 268 } 269 270 void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) { 271 SkPaint paint; 272 273 fInverse.mapPoints(pts, 2); 274 275 if (fGrid) { 276 apply_grid(pts, 2); 277 } 278 279 erase(fMinSurface); 280 this->setupPaint(&paint); 281 paint.setColor(FAT_PIXEL_COLOR); 282 if (fUseClip) { 283 fMinSurface->getCanvas()->save(); 284 SkRect r = fClipRect; 285 r.inset(SK_Scalar1/3, SK_Scalar1/3); 286 fMinSurface->getCanvas()->clipRect(r, SkRegion::kIntersect_Op, true); 287 } 288 fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); 289 if (fUseClip) { 290 fMinSurface->getCanvas()->restore(); 291 } 292 this->copyMinToMax(); 293 294 SkCanvas* max = fMaxSurface->getCanvas(); 295 296 fMatrix.mapPoints(pts, 2); 297 this->drawLineSkeleton(max, pts); 298 299 fMaxSurface->draw(canvas, 0, 0, nullptr); 300 } 301 302 void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) { 303 SkPaint paint; 304 305 fInverse.mapPoints(pts, 2); 306 307 if (fGrid) { 308 apply_grid(pts, 2); 309 } 310 311 SkRect r; 312 r.set(pts, 2); 313 314 erase(fMinSurface); 315 this->setupPaint(&paint); 316 paint.setColor(FAT_PIXEL_COLOR); 317 { 318 SkCanvas* c = fMinSurface->getCanvas(); 319 fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint); 320 } 321 this->copyMinToMax(); 322 323 SkCanvas* max = fMaxSurface->getCanvas(); 324 325 fMatrix.mapPoints(pts, 2); 326 r.set(pts, 2); 327 this->drawRectSkeleton(max, r); 328 329 fMaxSurface->draw(canvas, 0, 0, nullptr); 330 } 331 332 void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) { 333 SkPaint paint; 334 this->setupSkeletonPaint(&paint); 335 336 SkPath path; 337 path.moveTo(pts[0]); 338 path.lineTo(pts[1]); 339 path.lineTo(pts[2]); 340 path.close(); 341 342 max->drawPath(path, paint); 343 } 344 345 void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) { 346 SkPaint paint; 347 348 fInverse.mapPoints(pts, 3); 349 350 if (fGrid) { 351 apply_grid(pts, 3); 352 } 353 354 SkPath path; 355 path.moveTo(pts[0]); 356 path.lineTo(pts[1]); 357 path.lineTo(pts[2]); 358 path.close(); 359 360 erase(fMinSurface); 361 this->setupPaint(&paint); 362 paint.setColor(FAT_PIXEL_COLOR); 363 fMinSurface->getCanvas()->drawPath(path, paint); 364 this->copyMinToMax(); 365 366 SkCanvas* max = fMaxSurface->getCanvas(); 367 368 fMatrix.mapPoints(pts, 3); 369 this->drawTriangleSkeleton(max, pts); 370 371 fMaxSurface->draw(canvas, 0, 0, nullptr); 372 } 373 374 /////////////////////////////////////////////////////////////////////////////////////////////////// 375 376 class IndexClick : public SkView::Click { 377 int fIndex; 378 public: 379 IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {} 380 381 static int GetIndex(SkView::Click* click) { 382 return ((IndexClick*)click)->fIndex; 383 } 384 }; 385 386 class DrawLineView : public SampleView { 387 enum { 388 kZoom = 96 389 }; 390 391 FatBits fFB; 392 SkPoint fPts[3]; 393 bool fIsRect; 394 public: 395 DrawLineView() { 396 fFB.setWHZ(24, 16, kZoom); 397 fPts[0].set(1, 1); 398 fPts[1].set(5, 4); 399 fPts[2].set(2, 6); 400 SkMatrix::MakeScale(kZoom, kZoom).mapPoints(fPts, 3); 401 fIsRect = false; 402 } 403 404 void setStyle(FatBits::Style s) { 405 fFB.setStyle(s); 406 this->inval(nullptr); 407 } 408 409 protected: 410 bool onQuery(SkEvent* evt) override { 411 if (SampleCode::TitleQ(*evt)) { 412 SampleCode::TitleR(evt, "FatBits"); 413 return true; 414 } 415 SkUnichar uni; 416 if (SampleCode::CharQ(*evt, &uni)) { 417 switch (uni) { 418 case 'c': 419 fFB.setUseClip(!fFB.getUseClip()); 420 this->inval(nullptr); 421 return true; 422 case 'r': 423 fIsRect = !fIsRect; 424 this->inval(nullptr); 425 return true; 426 case 'o': 427 fFB.toggleRectAsOval(); 428 this->inval(nullptr); 429 return true; 430 case 'x': 431 fFB.setGrid(!fFB.getGrid()); 432 this->inval(nullptr); 433 return true; 434 case 's': 435 if (FatBits::kStroke_Style == fFB.getStyle()) { 436 this->setStyle(FatBits::kHair_Style); 437 } else { 438 this->setStyle(FatBits::kStroke_Style); 439 } 440 return true; 441 case 'k': { 442 const SkPaint::Cap caps[] = { 443 SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap, 444 }; 445 fFB.fStrokeCap = caps[(fFB.fStrokeCap + 1) % 3]; 446 this->inval(nullptr); 447 return true; 448 } break; 449 case 'a': 450 fFB.setAA(!fFB.getAA()); 451 this->inval(nullptr); 452 return true; 453 case 'w': 454 fFB.setShowSkeleton(!fFB.getShowSkeleton()); 455 this->inval(nullptr); 456 return true; 457 case 'g': 458 fFB.setUseGPU(!fFB.getUseGPU()); 459 this->inval(nullptr); 460 return true; 461 case 't': 462 fFB.setTriangle(!fFB.getTriangle()); 463 this->inval(nullptr); 464 return true; 465 } 466 } 467 return this->INHERITED::onQuery(evt); 468 } 469 470 void onDrawContent(SkCanvas* canvas) override { 471 fFB.drawBG(canvas); 472 if (fFB.getTriangle()) { 473 fFB.drawTriangle(canvas, fPts); 474 } 475 else if (fIsRect) { 476 fFB.drawRect(canvas, fPts); 477 } else { 478 fFB.drawLine(canvas, fPts); 479 } 480 fFB.drawFG(canvas); 481 482 { 483 SkString str; 484 str.printf("%s %s %s %s", 485 fFB.getAA() ? "AA" : "BW", 486 FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke", 487 fFB.getUseGPU() ? "GPU" : "CPU", 488 fFB.getUseClip() ? "clip" : "noclip"); 489 SkPaint paint; 490 paint.setAntiAlias(true); 491 paint.setTextSize(16); 492 paint.setColor(SK_ColorBLUE); 493 canvas->drawText(str.c_str(), str.size(), 10, 16, paint); 494 } 495 } 496 497 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override { 498 SkPoint pt = { x, y }; 499 int index = -1; 500 int count = fFB.getTriangle() ? 3 : 2; 501 SkScalar tol = 12; 502 503 for (int i = 0; i < count; ++i) { 504 if (fPts[i].equalsWithinTolerance(pt, tol)) { 505 index = i; 506 break; 507 } 508 } 509 return new IndexClick(this, index); 510 } 511 512 bool onClick(Click* click) override { 513 int index = IndexClick::GetIndex(click); 514 if (index >= 0 && index <= 2) { 515 fPts[index] = click->fCurr; 516 } else { 517 SkScalar dx = click->fCurr.fX - click->fPrev.fX; 518 SkScalar dy = click->fCurr.fY - click->fPrev.fY; 519 fPts[0].offset(dx, dy); 520 fPts[1].offset(dx, dy); 521 fPts[2].offset(dx, dy); 522 } 523 this->inval(nullptr); 524 return true; 525 } 526 527 private: 528 529 typedef SampleView INHERITED; 530 }; 531 532 ////////////////////////////////////////////////////////////////////////////// 533 534 static SkView* MyFactory() { return new DrawLineView; } 535 static SkViewRegister reg(MyFactory); 536