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