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 "SkPointPriv.h" 15#include "SkRandom.h" 16#include "SkRRect.h" 17#include "SkColorPriv.h" 18#include "SkStrokerPriv.h" 19#include "SkSurface.h" 20 21static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) { 22 const SkScalar TOL = 7; 23 return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL; 24} 25 26static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) { 27 SkPath::RawIter iter(path); 28 SkPoint pts[4]; 29 SkPath::Verb verb; 30 31 int count = 0; 32 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 33 switch (verb) { 34 case SkPath::kMove_Verb: 35 case SkPath::kLine_Verb: 36 case SkPath::kQuad_Verb: 37 case SkPath::kConic_Verb: 38 case SkPath::kCubic_Verb: 39 storage[count++] = pts[0]; 40 break; 41 default: 42 break; 43 } 44 } 45 return count; 46} 47 48static void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) { 49 SkPath::RawIter iter(path); 50 SkPoint pts[4]; 51 SkPath::Verb verb; 52 53 int count = 0; 54 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 55 switch (verb) { 56 case SkPath::kMove_Verb: 57 case SkPath::kLine_Verb: 58 count += 1; 59 break; 60 case SkPath::kQuad_Verb: 61 case SkPath::kConic_Verb: 62 count += 2; 63 break; 64 case SkPath::kCubic_Verb: 65 count += 3; 66 break; 67 case SkPath::kClose_Verb: 68 contourCounts->push_back(count); 69 count = 0; 70 break; 71 default: 72 break; 73 } 74 } 75 if (count > 0) { 76 contourCounts->push_back(count); 77 } 78} 79 80static void erase(const sk_sp<SkSurface>& surface) { 81 SkCanvas* canvas = surface->getCanvas(); 82 if (canvas) { 83 canvas->clear(SK_ColorTRANSPARENT); 84 } 85} 86 87struct StrokeTypeButton { 88 SkRect fBounds; 89 char fLabel; 90 bool fEnabled; 91}; 92 93struct CircleTypeButton : public StrokeTypeButton { 94 bool fFill; 95}; 96 97class QuadStrokerView : public SampleView { 98 enum { 99 SKELETON_COLOR = 0xFF0000FF, 100 WIREFRAME_COLOR = 0x80FF0000 101 }; 102 103 enum { 104 kCount = 18 105 }; 106 SkPoint fPts[kCount]; 107 SkRect fWeightControl; 108 SkRect fRadiusControl; 109 SkRect fErrorControl; 110 SkRect fWidthControl; 111 SkRect fBounds; 112 SkMatrix fMatrix, fInverse; 113 sk_sp<SkShader> fShader; 114 sk_sp<SkSurface> fMinSurface; 115 sk_sp<SkSurface> fMaxSurface; 116 StrokeTypeButton fCubicButton; 117 StrokeTypeButton fConicButton; 118 StrokeTypeButton fQuadButton; 119 StrokeTypeButton fArcButton; 120 StrokeTypeButton fRRectButton; 121 CircleTypeButton fCircleButton; 122 StrokeTypeButton fTextButton; 123 SkString fText; 124 SkScalar fTextSize; 125 SkScalar fWeight; 126 SkScalar fRadius; 127 SkScalar fWidth, fDWidth; 128 SkScalar fWidthScale; 129 int fW, fH, fZoom; 130 bool fAnimate; 131 bool fDrawRibs; 132 bool fDrawTangents; 133 bool fDrawTDivs; 134#ifdef SK_DEBUG 135 #define kStrokerErrorMin 0.001f 136 #define kStrokerErrorMax 5 137#endif 138 #define kWidthMin 1 139 #define kWidthMax 100 140public: 141 QuadStrokerView() { 142 this->setBGColor(SK_ColorLTGRAY); 143 144 fPts[0].set(50, 200); // cubic 145 fPts[1].set(50, 100); 146 fPts[2].set(150, 50); 147 fPts[3].set(300, 50); 148 149 fPts[4].set(350, 200); // conic 150 fPts[5].set(350, 100); 151 fPts[6].set(450, 50); 152 153 fPts[7].set(150, 300); // quad 154 fPts[8].set(150, 200); 155 fPts[9].set(250, 150); 156 157 fPts[10].set(250, 200); // arc 158 fPts[11].set(250, 300); 159 fPts[12].set(150, 350); 160 161 fPts[13].set(200, 200); // rrect 162 fPts[14].set(400, 400); 163 164 fPts[15].set(250, 250); // oval 165 fPts[16].set(450, 450); 166 167 fText = "a"; 168 fTextSize = 12; 169 fWidth = 50; 170 fDWidth = 0.25f; 171 fWeight = 1; 172 fRadius = 150; 173 174 fCubicButton.fLabel = 'C'; 175 fCubicButton.fEnabled = false; 176 fConicButton.fLabel = 'K'; 177 fConicButton.fEnabled = false; 178 fQuadButton.fLabel = 'Q'; 179 fQuadButton.fEnabled = false; 180 fArcButton.fLabel = 'A'; 181 fArcButton.fEnabled = true; 182 fRRectButton.fLabel = 'R'; 183 fRRectButton.fEnabled = false; 184 fCircleButton.fLabel = 'O'; 185 fCircleButton.fEnabled = true; 186 fCircleButton.fFill = true; 187 fTextButton.fLabel = 'T'; 188 fTextButton.fEnabled = false; 189 fAnimate = false; 190 setAsNeeded(); 191 } 192 193protected: 194 bool onQuery(SkEvent* evt) override { 195 if (SampleCode::TitleQ(*evt)) { 196 SampleCode::TitleR(evt, "QuadStroker"); 197 return true; 198 } 199 SkUnichar uni; 200 if (fTextButton.fEnabled && SampleCode::CharQ(*evt, &uni)) { 201 switch (uni) { 202 case ' ': 203 fText = ""; 204 break; 205 case '-': 206 fTextSize = SkTMax(1.0f, fTextSize - 1); 207 break; 208 case '+': 209 case '=': 210 fTextSize += 1; 211 break; 212 default: 213 fText.appendUnichar(uni); 214 } 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 SkPointPriv::RotateCCW(&tan); 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 SkPointPriv::RotateCCW(&tan); 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 SkPointPriv::RotateCCW(before, &beforeCCW); 560 SkPointPriv::RotateCCW(after, &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 SkPointPriv::RotateCW(before, &beforeCW); 570 SkPointPriv::RotateCW(after, &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 } 700 701 class MyClick : public Click { 702 public: 703 int fIndex; 704 MyClick(SkView* target, int index) : Click(target), fIndex(index) {} 705 }; 706 707 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, 708 unsigned modi) override { 709 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { 710 if (hittest(fPts[i], x, y)) { 711 return new MyClick(this, (int)i); 712 } 713 } 714 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); 715 if (fWeightControl.contains(rectPt)) { 716 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); 717 } 718 if (fRadiusControl.contains(rectPt)) { 719 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2); 720 } 721#ifdef SK_DEBUG 722 if (fErrorControl.contains(rectPt)) { 723 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); 724 } 725#endif 726 if (fWidthControl.contains(rectPt)) { 727 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); 728 } 729 if (fCubicButton.fBounds.contains(rectPt)) { 730 fCubicButton.fEnabled ^= true; 731 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); 732 } 733 if (fConicButton.fBounds.contains(rectPt)) { 734 fConicButton.fEnabled ^= true; 735 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); 736 } 737 if (fQuadButton.fBounds.contains(rectPt)) { 738 fQuadButton.fEnabled ^= true; 739 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7); 740 } 741 if (fArcButton.fBounds.contains(rectPt)) { 742 fArcButton.fEnabled ^= true; 743 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8); 744 } 745 if (fRRectButton.fBounds.contains(rectPt)) { 746 fRRectButton.fEnabled ^= true; 747 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9); 748 } 749 if (fCircleButton.fBounds.contains(rectPt)) { 750 bool wasEnabled = fCircleButton.fEnabled; 751 fCircleButton.fEnabled = !fCircleButton.fFill; 752 fCircleButton.fFill = wasEnabled && !fCircleButton.fFill; 753 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 10); 754 } 755 if (fTextButton.fBounds.contains(rectPt)) { 756 fTextButton.fEnabled ^= true; 757 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 11); 758 } 759 return this->INHERITED::onFindClickHandler(x, y, modi); 760 } 761 762 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min, 763 SkScalar max) { 764 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min; 765 } 766 767 bool onClick(Click* click) override { 768 int index = ((MyClick*)click)->fIndex; 769 if (index < (int) SK_ARRAY_COUNT(fPts)) { 770 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX), 771 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY)); 772 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) { 773 fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5); 774 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) { 775 fRadius = MapScreenYtoValue(click->fICurr.fY, fRadiusControl, 0, 500); 776 } 777#ifdef SK_DEBUG 778 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) { 779 gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, 780 fErrorControl, kStrokerErrorMin, kStrokerErrorMax)); 781 gDebugStrokerErrorSet = true; 782 } 783#endif 784 else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) { 785 fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl, 786 kWidthMin, kWidthMax)); 787 fAnimate = fWidth <= kWidthMin; 788 } 789 return true; 790 } 791 792private: 793 typedef SkView INHERITED; 794}; 795 796/////////////////////////////////////////////////////////////////////////////// 797 798static SkView* F2() { return new QuadStrokerView; } 799static SkViewRegister gR2(F2); 800