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