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 "SkColor.h" 12 #include "SkFont.h" 13 #include "SkGeometry.h" 14 #include "SkImageInfo.h" 15 #include "SkMatrix.h" 16 #include "SkPaint.h" 17 #include "SkPath.h" 18 #include "SkPathMeasure.h" 19 #include "SkPoint.h" 20 #include "SkPointPriv.h" 21 #include "SkRRect.h" 22 #include "SkRect.h" 23 #include "SkRefCnt.h" 24 #include "SkScalar.h" 25 #include "SkShader.h" 26 #include "SkString.h" 27 #include "SkStroke.h" 28 #include "SkSurface.h" 29 #include "SkTArray.h" 30 #include "SkTemplates.h" 31 #include "SkTextUtils.h" 32 #include "SkTypes.h" 33 #include "sk_tool_utils.h" 34 35 #include <cfloat> 36 37 class SkEvent; 38 39 static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) { 40 const SkScalar TOL = 7; 41 return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL; 42 } 43 44 static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) { 45 SkPath::RawIter iter(path); 46 SkPoint pts[4]; 47 SkPath::Verb verb; 48 49 int count = 0; 50 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 51 switch (verb) { 52 case SkPath::kMove_Verb: 53 case SkPath::kLine_Verb: 54 case SkPath::kQuad_Verb: 55 case SkPath::kConic_Verb: 56 case SkPath::kCubic_Verb: 57 storage[count++] = pts[0]; 58 break; 59 default: 60 break; 61 } 62 } 63 return count; 64 } 65 66 static void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) { 67 SkPath::RawIter iter(path); 68 SkPoint pts[4]; 69 SkPath::Verb verb; 70 71 int count = 0; 72 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 73 switch (verb) { 74 case SkPath::kMove_Verb: 75 case SkPath::kLine_Verb: 76 count += 1; 77 break; 78 case SkPath::kQuad_Verb: 79 case SkPath::kConic_Verb: 80 count += 2; 81 break; 82 case SkPath::kCubic_Verb: 83 count += 3; 84 break; 85 case SkPath::kClose_Verb: 86 contourCounts->push_back(count); 87 count = 0; 88 break; 89 default: 90 break; 91 } 92 } 93 if (count > 0) { 94 contourCounts->push_back(count); 95 } 96 } 97 98 static void erase(const sk_sp<SkSurface>& surface) { 99 SkCanvas* canvas = surface->getCanvas(); 100 if (canvas) { 101 canvas->clear(SK_ColorTRANSPARENT); 102 } 103 } 104 105 struct StrokeTypeButton { 106 SkRect fBounds; 107 char fLabel; 108 bool fEnabled; 109 }; 110 111 struct CircleTypeButton : public StrokeTypeButton { 112 bool fFill; 113 }; 114 115 class QuadStrokerView : public Sample { 116 enum { 117 SKELETON_COLOR = 0xFF0000FF, 118 WIREFRAME_COLOR = 0x80FF0000 119 }; 120 121 enum { 122 kCount = 18 123 }; 124 SkPoint fPts[kCount]; 125 SkRect fWeightControl; 126 SkRect fRadiusControl; 127 SkRect fErrorControl; 128 SkRect fWidthControl; 129 SkRect fBounds; 130 SkMatrix fMatrix, fInverse; 131 sk_sp<SkShader> fShader; 132 sk_sp<SkSurface> fMinSurface; 133 sk_sp<SkSurface> fMaxSurface; 134 StrokeTypeButton fCubicButton; 135 StrokeTypeButton fConicButton; 136 StrokeTypeButton fQuadButton; 137 StrokeTypeButton fArcButton; 138 StrokeTypeButton fRRectButton; 139 CircleTypeButton fCircleButton; 140 StrokeTypeButton fTextButton; 141 SkString fText; 142 SkScalar fTextSize; 143 SkScalar fWeight; 144 SkScalar fRadius; 145 SkScalar fWidth, fDWidth; 146 SkScalar fWidthScale; 147 int fW, fH, fZoom; 148 bool fAnimate; 149 bool fDrawRibs; 150 bool fDrawTangents; 151 bool fDrawTDivs; 152 #ifdef SK_DEBUG 153 #define kStrokerErrorMin 0.001f 154 #define kStrokerErrorMax 5 155 #endif 156 #define kWidthMin 1 157 #define kWidthMax 100 158 public: 159 QuadStrokerView() { 160 this->setBGColor(SK_ColorLTGRAY); 161 162 fPts[0].set(50, 200); // cubic 163 fPts[1].set(50, 100); 164 fPts[2].set(150, 50); 165 fPts[3].set(300, 50); 166 167 fPts[4].set(350, 200); // conic 168 fPts[5].set(350, 100); 169 fPts[6].set(450, 50); 170 171 fPts[7].set(150, 300); // quad 172 fPts[8].set(150, 200); 173 fPts[9].set(250, 150); 174 175 fPts[10].set(250, 200); // arc 176 fPts[11].set(250, 300); 177 fPts[12].set(150, 350); 178 179 fPts[13].set(200, 200); // rrect 180 fPts[14].set(400, 400); 181 182 fPts[15].set(250, 250); // oval 183 fPts[16].set(450, 450); 184 185 fText = "a"; 186 fTextSize = 12; 187 fWidth = 50; 188 fDWidth = 0.25f; 189 fWeight = 1; 190 fRadius = 150; 191 192 fCubicButton.fLabel = 'C'; 193 fCubicButton.fEnabled = false; 194 fConicButton.fLabel = 'K'; 195 fConicButton.fEnabled = false; 196 fQuadButton.fLabel = 'Q'; 197 fQuadButton.fEnabled = false; 198 fArcButton.fLabel = 'A'; 199 fArcButton.fEnabled = true; 200 fRRectButton.fLabel = 'R'; 201 fRRectButton.fEnabled = false; 202 fCircleButton.fLabel = 'O'; 203 fCircleButton.fEnabled = true; 204 fCircleButton.fFill = true; 205 fTextButton.fLabel = 'T'; 206 fTextButton.fEnabled = false; 207 fAnimate = false; 208 setAsNeeded(); 209 } 210 211 protected: 212 bool onQuery(Sample::Event* evt) override { 213 if (Sample::TitleQ(*evt)) { 214 Sample::TitleR(evt, "QuadStroker"); 215 return true; 216 } 217 SkUnichar uni; 218 if (fTextButton.fEnabled && Sample::CharQ(*evt, &uni)) { 219 switch (uni) { 220 case ' ': 221 fText = ""; 222 break; 223 case '-': 224 fTextSize = SkTMax(1.0f, fTextSize - 1); 225 break; 226 case '+': 227 case '=': 228 fTextSize += 1; 229 break; 230 default: 231 fText.appendUnichar(uni); 232 } 233 return true; 234 } 235 return this->INHERITED::onQuery(evt); 236 } 237 238 void onSizeChange() override { 239 fRadiusControl.setXYWH(this->width() - 200, 30, 30, 400); 240 fWeightControl.setXYWH(this->width() - 150, 30, 30, 400); 241 fErrorControl.setXYWH(this->width() - 100, 30, 30, 400); 242 fWidthControl.setXYWH(this->width() - 50, 30, 30, 400); 243 int buttonOffset = 450; 244 fCubicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 245 buttonOffset += 50; 246 fConicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 247 buttonOffset += 50; 248 fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 249 buttonOffset += 50; 250 fArcButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 251 buttonOffset += 50; 252 fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 253 buttonOffset += 50; 254 fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 255 buttonOffset += 50; 256 fTextButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30); 257 this->INHERITED::onSizeChange(); 258 } 259 260 void copyMinToMax() { 261 erase(fMaxSurface); 262 SkCanvas* canvas = fMaxSurface->getCanvas(); 263 canvas->save(); 264 canvas->concat(fMatrix); 265 fMinSurface->draw(canvas, 0, 0, nullptr); 266 canvas->restore(); 267 268 SkPaint paint; 269 paint.setBlendMode(SkBlendMode::kClear); 270 for (int iy = 1; iy < fH; ++iy) { 271 SkScalar y = SkIntToScalar(iy * fZoom); 272 canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint); 273 } 274 for (int ix = 1; ix < fW; ++ix) { 275 SkScalar x = SkIntToScalar(ix * fZoom); 276 canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint); 277 } 278 } 279 280 void setWHZ(int width, int height, int zoom) { 281 fZoom = zoom; 282 fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom)); 283 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom)); 284 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom); 285 fShader = sk_tool_utils::create_checkerboard_shader(0xFFCCCCCC, 0xFFFFFFFF, zoom); 286 287 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 288 fMinSurface = SkSurface::MakeRaster(info); 289 info = info.makeWH(width * zoom, height * zoom); 290 fMaxSurface = SkSurface::MakeRaster(info); 291 } 292 293 void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color, 294 bool show_lines) { 295 SkPaint paint; 296 paint.setColor(color); 297 paint.setAlpha(0x80); 298 paint.setAntiAlias(true); 299 int n = path.countPoints(); 300 SkAutoSTArray<32, SkPoint> pts(n); 301 if (show_lines && fDrawTangents) { 302 SkTArray<int> contourCounts; 303 getContourCounts(path, &contourCounts); 304 SkPoint* ptPtr = pts.get(); 305 for (int i = 0; i < contourCounts.count(); ++i) { 306 int count = contourCounts[i]; 307 path.getPoints(ptPtr, count); 308 canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint); 309 ptPtr += count; 310 } 311 } else { 312 n = getOnCurvePoints(path, pts.get()); 313 } 314 paint.setStrokeWidth(5); 315 canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint); 316 } 317 318 void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width, 319 SkColor color) { 320 const SkScalar radius = width / 2; 321 322 SkPathMeasure meas(path, false); 323 SkScalar total = meas.getLength(); 324 325 SkScalar delta = 8; 326 SkPaint paint, labelP; 327 paint.setColor(color); 328 labelP.setColor(color & 0xff5f9f5f); 329 SkFont font; 330 SkPoint pos, tan; 331 int index = 0; 332 for (SkScalar dist = 0; dist <= total; dist += delta) { 333 if (meas.getPosTan(dist, &pos, &tan)) { 334 tan.scale(radius); 335 SkPointPriv::RotateCCW(&tan); 336 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), 337 pos.x() - tan.x(), pos.y() - tan.y(), paint); 338 if (0 == index % 10) { 339 SkString label; 340 label.appendS32(index); 341 SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4); 342 canvas->drawRect(dot, labelP); 343 canvas->drawString(label, 344 pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, font, labelP); 345 } 346 } 347 ++index; 348 } 349 } 350 351 void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) { 352 const SkScalar radius = width / 2; 353 SkPaint paint; 354 paint.setColor(color); 355 SkPathMeasure meas(path, false); 356 SkScalar total = meas.getLength(); 357 SkScalar delta = 8; 358 int ribs = 0; 359 for (SkScalar dist = 0; dist <= total; dist += delta) { 360 ++ribs; 361 } 362 SkPath::RawIter iter(path); 363 SkPoint pts[4]; 364 if (SkPath::kMove_Verb != iter.next(pts)) { 365 SkASSERT(0); 366 return; 367 } 368 SkPath::Verb verb = iter.next(pts); 369 SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb); 370 SkPoint pos, tan; 371 for (int index = 0; index < ribs; ++index) { 372 SkScalar t = (SkScalar) index / ribs; 373 switch (verb) { 374 case SkPath::kLine_Verb: 375 tan = pts[1] - pts[0]; 376 pos = pts[0]; 377 pos.fX += tan.fX * t; 378 pos.fY += tan.fY * t; 379 break; 380 case SkPath::kQuad_Verb: 381 pos = SkEvalQuadAt(pts, t); 382 tan = SkEvalQuadTangentAt(pts, t); 383 break; 384 case SkPath::kConic_Verb: { 385 SkConic conic(pts, iter.conicWeight()); 386 pos = conic.evalAt(t); 387 tan = conic.evalTangentAt(t); 388 } break; 389 case SkPath::kCubic_Verb: 390 SkEvalCubicAt(pts, t, &pos, &tan, nullptr); 391 break; 392 default: 393 SkASSERT(0); 394 return; 395 } 396 tan.setLength(radius); 397 SkPointPriv::RotateCCW(&tan); 398 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), 399 pos.x() - tan.x(), pos.y() - tan.y(), paint); 400 if (0 == index % 10) { 401 SkString label; 402 label.appendS32(index); 403 canvas->drawString(label, 404 pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, SkFont(), paint); 405 } 406 } 407 } 408 409 void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale, 410 bool drawText) { 411 if (path.isEmpty()) { 412 return; 413 } 414 SkRect bounds = path.getBounds(); 415 this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText 416 ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale), 417 SkScalarRoundToInt(950.0f / scale)); 418 erase(fMinSurface); 419 SkPaint paint; 420 paint.setColor(0x1f1f0f0f); 421 paint.setStyle(SkPaint::kStroke_Style); 422 paint.setStrokeWidth(width * scale * scale); 423 paint.setColor(0x3f0f1f3f); 424 if (drawText) { 425 fMinSurface->getCanvas()->drawPath(path, paint); 426 this->copyMinToMax(); 427 fMaxSurface->draw(canvas, 0, 0, nullptr); 428 } 429 paint.setAntiAlias(true); 430 paint.setStyle(SkPaint::kStroke_Style); 431 paint.setStrokeWidth(1); 432 433 paint.setColor(SKELETON_COLOR); 434 SkPath scaled; 435 SkMatrix matrix; 436 matrix.reset(); 437 matrix.setScale(950 / scale, 950 / scale); 438 if (drawText) { 439 path.transform(matrix, &scaled); 440 } else { 441 scaled = path; 442 } 443 canvas->drawPath(scaled, paint); 444 draw_points(canvas, scaled, SKELETON_COLOR, true); 445 446 if (fDrawRibs) { 447 draw_ribs(canvas, scaled, width, 0xFF00FF00); 448 } 449 450 if (fDrawTDivs) { 451 draw_t_divs(canvas, scaled, width, 0xFF3F3F00); 452 } 453 454 SkPath fill; 455 456 SkPaint p; 457 p.setStyle(SkPaint::kStroke_Style); 458 if (drawText) { 459 p.setStrokeWidth(width * scale * scale); 460 } else { 461 p.setStrokeWidth(width); 462 } 463 p.getFillPath(path, &fill); 464 SkPath scaledFill; 465 if (drawText) { 466 fill.transform(matrix, &scaledFill); 467 } else { 468 scaledFill = fill; 469 } 470 paint.setColor(WIREFRAME_COLOR); 471 canvas->drawPath(scaledFill, paint); 472 draw_points(canvas, scaledFill, WIREFRAME_COLOR, false); 473 } 474 475 void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) { 476 if (rect.isEmpty()) { 477 return; 478 } 479 SkPaint paint; 480 paint.setColor(0x1f1f0f0f); 481 paint.setStyle(SkPaint::kStroke_Style); 482 paint.setStrokeWidth(width); 483 SkPath path; 484 SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2; 485 SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide }; 486 path.addCircle(center.fX, center.fY, maxSide); 487 canvas->drawPath(path, paint); 488 paint.setStyle(SkPaint::kFill_Style); 489 path.reset(); 490 path.addCircle(center.fX, center.fY, maxSide - width / 2); 491 paint.setColor(0x3f0f1f3f); 492 canvas->drawPath(path, paint); 493 path.reset(); 494 path.setFillType(SkPath::kEvenOdd_FillType); 495 path.addCircle(center.fX, center.fY, maxSide + width / 2); 496 SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width, 497 (maxSide + width) * 2, (maxSide + width) * 2); 498 path.addRect(outside); 499 canvas->drawPath(path, paint); 500 } 501 502 void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) { 503 SkPaint paint; 504 paint.setAntiAlias(true); 505 paint.setStyle(SkPaint::kStroke_Style); 506 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); 507 canvas->drawRect(button.fBounds, paint); 508 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); 509 paint.setStyle(SkPaint::kFill_Style); 510 SkFont font; 511 font.setSize(25.0f); 512 SkTextUtils::Draw(canvas, &button.fLabel, 1, kUTF8_SkTextEncoding, 513 button.fBounds.centerX(), button.fBounds.fBottom - 5, 514 font, paint, SkTextUtils::kCenter_Align); 515 } 516 517 void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value, 518 SkScalar min, SkScalar max, const char* name) { 519 SkPaint paint; 520 paint.setAntiAlias(true); 521 paint.setStyle(SkPaint::kStroke_Style); 522 canvas->drawRect(bounds, paint); 523 SkScalar scale = max - min; 524 SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale; 525 paint.setColor(0xFFFF0000); 526 canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint); 527 SkString label; 528 label.printf("%0.3g", value); 529 paint.setColor(0xFF000000); 530 paint.setStyle(SkPaint::kFill_Style); 531 SkFont font(nullptr, 11.0f); 532 canvas->drawString(label, bounds.fLeft + 5, yPos - 5, font, paint); 533 font.setSize(13.0f); 534 canvas->drawString(name, bounds.fLeft, bounds.bottom() + 11, font, paint); 535 } 536 537 void setForGeometry() { 538 fDrawRibs = true; 539 fDrawTangents = true; 540 fDrawTDivs = false; 541 fWidthScale = 1; 542 } 543 544 void setForText() { 545 fDrawRibs = fDrawTangents = fDrawTDivs = false; 546 fWidthScale = 0.002f; 547 } 548 549 void setForSingles() { 550 setForGeometry(); 551 fDrawTDivs = true; 552 } 553 554 void setAsNeeded() { 555 if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) { 556 setForSingles(); 557 } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) { 558 setForGeometry(); 559 } else { 560 setForText(); 561 } 562 } 563 564 bool arcCenter(SkPoint* center) { 565 SkPath path; 566 path.moveTo(fPts[10]); 567 path.arcTo(fPts[11], fPts[12], fRadius); 568 SkPath::Iter iter(path, false); 569 SkPoint pts[4]; 570 iter.next(pts); 571 if (SkPath::kLine_Verb == iter.next(pts)) { 572 iter.next(pts); 573 } 574 SkVector before = pts[0] - pts[1]; 575 SkVector after = pts[1] - pts[2]; 576 before.setLength(fRadius); 577 after.setLength(fRadius); 578 SkVector beforeCCW, afterCCW; 579 SkPointPriv::RotateCCW(before, &beforeCCW); 580 SkPointPriv::RotateCCW(after, &afterCCW); 581 beforeCCW += pts[0]; 582 afterCCW += pts[2]; 583 *center = beforeCCW; 584 if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX) 585 && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) { 586 return true; 587 } 588 SkVector beforeCW, afterCW; 589 SkPointPriv::RotateCW(before, &beforeCW); 590 SkPointPriv::RotateCW(after, &afterCW); 591 beforeCW += pts[0]; 592 afterCW += pts[2]; 593 *center = beforeCW; 594 return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX) 595 && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY); 596 } 597 598 void onDrawContent(SkCanvas* canvas) override { 599 SkPath path; 600 SkScalar width = fWidth; 601 602 if (fCubicButton.fEnabled) { 603 path.moveTo(fPts[0]); 604 path.cubicTo(fPts[1], fPts[2], fPts[3]); 605 setForSingles(); 606 draw_stroke(canvas, path, width, 950, false); 607 } 608 609 if (fConicButton.fEnabled) { 610 path.reset(); 611 path.moveTo(fPts[4]); 612 path.conicTo(fPts[5], fPts[6], fWeight); 613 setForSingles(); 614 draw_stroke(canvas, path, width, 950, false); 615 } 616 617 if (fQuadButton.fEnabled) { 618 path.reset(); 619 path.moveTo(fPts[7]); 620 path.quadTo(fPts[8], fPts[9]); 621 setForSingles(); 622 draw_stroke(canvas, path, width, 950, false); 623 } 624 625 if (fArcButton.fEnabled) { 626 path.reset(); 627 path.moveTo(fPts[10]); 628 path.arcTo(fPts[11], fPts[12], fRadius); 629 setForGeometry(); 630 draw_stroke(canvas, path, width, 950, false); 631 SkPath pathPts; 632 pathPts.moveTo(fPts[10]); 633 pathPts.lineTo(fPts[11]); 634 pathPts.lineTo(fPts[12]); 635 draw_points(canvas, pathPts, SK_ColorDKGRAY, true); 636 } 637 638 if (fRRectButton.fEnabled) { 639 SkScalar rad = 32; 640 SkRect r; 641 r.set(&fPts[13], 2); 642 path.reset(); 643 SkRRect rr; 644 rr.setRectXY(r, rad, rad); 645 path.addRRect(rr); 646 setForGeometry(); 647 draw_stroke(canvas, path, width, 950, false); 648 649 path.reset(); 650 SkRRect rr2; 651 rr.inset(width/2, width/2, &rr2); 652 path.addRRect(rr2, SkPath::kCCW_Direction); 653 rr.inset(-width/2, -width/2, &rr2); 654 path.addRRect(rr2, SkPath::kCW_Direction); 655 SkPaint paint; 656 paint.setAntiAlias(true); 657 paint.setColor(0x40FF8844); 658 canvas->drawPath(path, paint); 659 } 660 661 if (fCircleButton.fEnabled) { 662 path.reset(); 663 SkRect r; 664 r.set(&fPts[15], 2); 665 path.addOval(r); 666 setForGeometry(); 667 if (fCircleButton.fFill) { 668 if (fArcButton.fEnabled) { 669 SkPoint center; 670 if (arcCenter(¢er)) { 671 r.set(center.fX - fRadius, center.fY - fRadius, center.fX + fRadius, 672 center.fY + fRadius); 673 } 674 } 675 draw_fill(canvas, r, width); 676 } else { 677 draw_stroke(canvas, path, width, 950, false); 678 } 679 } 680 681 if (fTextButton.fEnabled) { 682 path.reset(); 683 SkFont font; 684 font.setSize(fTextSize); 685 SkTextUtils::GetPath(fText.c_str(), fText.size(), kUTF8_SkTextEncoding, 686 0, fTextSize, font, &path); 687 setForText(); 688 draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true); 689 } 690 691 if (fAnimate) { 692 fWidth += fDWidth; 693 if (fDWidth > 0 && fWidth > kWidthMax) { 694 fDWidth = -fDWidth; 695 } else if (fDWidth < 0 && fWidth < kWidthMin) { 696 fDWidth = -fDWidth; 697 } 698 } 699 setAsNeeded(); 700 if (fConicButton.fEnabled) { 701 draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight"); 702 } 703 if (fArcButton.fEnabled) { 704 draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius"); 705 } 706 #ifdef SK_DEBUG 707 draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax, 708 "error"); 709 #endif 710 draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale, 711 kWidthMax * fWidthScale, "width"); 712 draw_button(canvas, fQuadButton); 713 draw_button(canvas, fCubicButton); 714 draw_button(canvas, fConicButton); 715 draw_button(canvas, fArcButton); 716 draw_button(canvas, fRRectButton); 717 draw_button(canvas, fCircleButton); 718 draw_button(canvas, fTextButton); 719 } 720 721 class MyClick : public Click { 722 public: 723 int fIndex; 724 MyClick(Sample* target, int index) : Click(target), fIndex(index) {} 725 }; 726 727 virtual Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, 728 unsigned modi) override { 729 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { 730 if (hittest(fPts[i], x, y)) { 731 return new MyClick(this, (int)i); 732 } 733 } 734 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); 735 if (fWeightControl.contains(rectPt)) { 736 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); 737 } 738 if (fRadiusControl.contains(rectPt)) { 739 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2); 740 } 741 #ifdef SK_DEBUG 742 if (fErrorControl.contains(rectPt)) { 743 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); 744 } 745 #endif 746 if (fWidthControl.contains(rectPt)) { 747 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); 748 } 749 if (fCubicButton.fBounds.contains(rectPt)) { 750 fCubicButton.fEnabled ^= true; 751 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); 752 } 753 if (fConicButton.fBounds.contains(rectPt)) { 754 fConicButton.fEnabled ^= true; 755 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); 756 } 757 if (fQuadButton.fBounds.contains(rectPt)) { 758 fQuadButton.fEnabled ^= true; 759 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7); 760 } 761 if (fArcButton.fBounds.contains(rectPt)) { 762 fArcButton.fEnabled ^= true; 763 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8); 764 } 765 if (fRRectButton.fBounds.contains(rectPt)) { 766 fRRectButton.fEnabled ^= true; 767 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9); 768 } 769 if (fCircleButton.fBounds.contains(rectPt)) { 770 bool wasEnabled = fCircleButton.fEnabled; 771 fCircleButton.fEnabled = !fCircleButton.fFill; 772 fCircleButton.fFill = wasEnabled && !fCircleButton.fFill; 773 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 10); 774 } 775 if (fTextButton.fBounds.contains(rectPt)) { 776 fTextButton.fEnabled ^= true; 777 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 11); 778 } 779 return this->INHERITED::onFindClickHandler(x, y, modi); 780 } 781 782 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min, 783 SkScalar max) { 784 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min; 785 } 786 787 bool onClick(Click* click) override { 788 int index = ((MyClick*)click)->fIndex; 789 if (index < (int) SK_ARRAY_COUNT(fPts)) { 790 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX), 791 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY)); 792 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) { 793 fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5); 794 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) { 795 fRadius = MapScreenYtoValue(click->fICurr.fY, fRadiusControl, 0, 500); 796 } 797 #ifdef SK_DEBUG 798 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) { 799 gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, 800 fErrorControl, kStrokerErrorMin, kStrokerErrorMax)); 801 gDebugStrokerErrorSet = true; 802 } 803 #endif 804 else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) { 805 fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl, 806 kWidthMin, kWidthMax)); 807 fAnimate = fWidth <= kWidthMin; 808 } 809 return true; 810 } 811 812 private: 813 typedef Sample INHERITED; 814 }; 815 816 /////////////////////////////////////////////////////////////////////////////// 817 818 DEF_SAMPLE( return new QuadStrokerView(); ) 819