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