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(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 SkAutoTUnref<SkShader> fShader; 113 SkAutoTUnref<SkSurface> fMinSurface; 114 SkAutoTUnref<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.setXfermodeMode(SkXfermode::kClear_Mode); 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.reset(sk_tool_utils::create_checkerboard_shader( 268 0xFFCCCCCC, 0xFFFFFFFF, zoom)); 269 270 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 271 fMinSurface.reset(SkSurface::NewRaster(info)); 272 info = info.makeWH(width * zoom, height * zoom); 273 fMaxSurface.reset(SkSurface::NewRaster(info)); 274 } 275 276 void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color, 277 bool show_lines) { 278 SkPaint paint; 279 paint.setColor(color); 280 paint.setAlpha(0x80); 281 paint.setAntiAlias(true); 282 int n = path.countPoints(); 283 SkAutoSTArray<32, SkPoint> pts(n); 284 if (show_lines && fDrawTangents) { 285 SkTArray<int> contourCounts; 286 getContourCounts(path, &contourCounts); 287 SkPoint* ptPtr = pts.get(); 288 for (int i = 0; i < contourCounts.count(); ++i) { 289 int count = contourCounts[i]; 290 path.getPoints(ptPtr, count); 291 canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint); 292 ptPtr += count; 293 } 294 } else { 295 n = getOnCurvePoints(path, pts.get()); 296 } 297 paint.setStrokeWidth(5); 298 canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint); 299 } 300 301 void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width, 302 SkColor color) { 303 const SkScalar radius = width / 2; 304 305 SkPathMeasure meas(path, false); 306 SkScalar total = meas.getLength(); 307 308 SkScalar delta = 8; 309 SkPaint paint, labelP; 310 paint.setColor(color); 311 labelP.setColor(color & 0xff5f9f5f); 312 SkPoint pos, tan; 313 int index = 0; 314 for (SkScalar dist = 0; dist <= total; dist += delta) { 315 if (meas.getPosTan(dist, &pos, &tan)) { 316 tan.scale(radius); 317 tan.rotateCCW(); 318 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), 319 pos.x() - tan.x(), pos.y() - tan.y(), paint); 320 if (0 == index % 10) { 321 SkString label; 322 label.appendS32(index); 323 SkRect dot = SkRect::MakeXYWH(pos.x() - 2, pos.y() - 2, 4, 4); 324 canvas->drawRect(dot, labelP); 325 canvas->drawText(label.c_str(), label.size(), 326 pos.x() - tan.x() * 1.25f, pos.y() - tan.y() * 1.25f, labelP); 327 } 328 } 329 ++index; 330 } 331 } 332 333 void draw_t_divs(SkCanvas* canvas, const SkPath& path, SkScalar width, SkColor color) { 334 const SkScalar radius = width / 2; 335 SkPaint paint; 336 paint.setColor(color); 337 SkPathMeasure meas(path, false); 338 SkScalar total = meas.getLength(); 339 SkScalar delta = 8; 340 int ribs = 0; 341 for (SkScalar dist = 0; dist <= total; dist += delta) { 342 ++ribs; 343 } 344 SkPath::RawIter iter(path); 345 SkPoint pts[4]; 346 if (SkPath::kMove_Verb != iter.next(pts)) { 347 SkASSERT(0); 348 return; 349 } 350 SkPath::Verb verb = iter.next(pts); 351 SkASSERT(SkPath::kLine_Verb <= verb && verb <= SkPath::kCubic_Verb); 352 SkPoint pos, tan; 353 for (int index = 0; index < ribs; ++index) { 354 SkScalar t = (SkScalar) index / ribs; 355 switch (verb) { 356 case SkPath::kLine_Verb: 357 tan = pts[1] - pts[0]; 358 pos = pts[0]; 359 pos.fX += tan.fX * t; 360 pos.fY += tan.fY * t; 361 break; 362 case SkPath::kQuad_Verb: 363 pos = SkEvalQuadAt(pts, t); 364 tan = SkEvalQuadTangentAt(pts, t); 365 break; 366 case SkPath::kConic_Verb: { 367 SkConic conic(pts, iter.conicWeight()); 368 pos = conic.evalAt(t); 369 tan = conic.evalTangentAt(t); 370 } break; 371 case SkPath::kCubic_Verb: 372 SkEvalCubicAt(pts, t, &pos, &tan, nullptr); 373 break; 374 default: 375 SkASSERT(0); 376 return; 377 } 378 tan.setLength(radius); 379 tan.rotateCCW(); 380 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), 381 pos.x() - tan.x(), pos.y() - tan.y(), paint); 382 if (0 == index % 10) { 383 SkString label; 384 label.appendS32(index); 385 canvas->drawText(label.c_str(), label.size(), 386 pos.x() + tan.x() * 1.25f, pos.y() + tan.y() * 1.25f, paint); 387 } 388 } 389 } 390 391 void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale, 392 bool drawText) { 393 if (path.isEmpty()) { 394 return; 395 } 396 SkRect bounds = path.getBounds(); 397 this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText 398 ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale), 399 SkScalarRoundToInt(950.0f / scale)); 400 erase(fMinSurface); 401 SkPaint paint; 402 paint.setColor(0x1f1f0f0f); 403 paint.setStyle(SkPaint::kStroke_Style); 404 paint.setStrokeWidth(width * scale * scale); 405 paint.setColor(0x3f0f1f3f); 406 if (drawText) { 407 fMinSurface->getCanvas()->drawPath(path, paint); 408 this->copyMinToMax(); 409 fMaxSurface->draw(canvas, 0, 0, nullptr); 410 } 411 paint.setAntiAlias(true); 412 paint.setStyle(SkPaint::kStroke_Style); 413 paint.setStrokeWidth(1); 414 415 paint.setColor(SKELETON_COLOR); 416 SkPath scaled; 417 SkMatrix matrix; 418 matrix.reset(); 419 matrix.setScale(950 / scale, 950 / scale); 420 if (drawText) { 421 path.transform(matrix, &scaled); 422 } else { 423 scaled = path; 424 } 425 canvas->drawPath(scaled, paint); 426 draw_points(canvas, scaled, SKELETON_COLOR, true); 427 428 if (fDrawRibs) { 429 draw_ribs(canvas, scaled, width, 0xFF00FF00); 430 } 431 432 if (fDrawTDivs) { 433 draw_t_divs(canvas, scaled, width, 0xFF3F3F00); 434 } 435 436 SkPath fill; 437 438 SkPaint p; 439 p.setStyle(SkPaint::kStroke_Style); 440 if (drawText) { 441 p.setStrokeWidth(width * scale * scale); 442 } else { 443 p.setStrokeWidth(width); 444 } 445 p.getFillPath(path, &fill); 446 SkPath scaledFill; 447 if (drawText) { 448 fill.transform(matrix, &scaledFill); 449 } else { 450 scaledFill = fill; 451 } 452 paint.setColor(WIREFRAME_COLOR); 453 canvas->drawPath(scaledFill, paint); 454 draw_points(canvas, scaledFill, WIREFRAME_COLOR, false); 455 } 456 457 void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) { 458 if (rect.isEmpty()) { 459 return; 460 } 461 SkPaint paint; 462 paint.setColor(0x1f1f0f0f); 463 paint.setStyle(SkPaint::kStroke_Style); 464 paint.setStrokeWidth(width); 465 SkPath path; 466 SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2; 467 SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide }; 468 path.addCircle(center.fX, center.fY, maxSide); 469 canvas->drawPath(path, paint); 470 paint.setStyle(SkPaint::kFill_Style); 471 path.reset(); 472 path.addCircle(center.fX, center.fY, maxSide - width / 2); 473 paint.setColor(0x3f0f1f3f); 474 canvas->drawPath(path, paint); 475 path.reset(); 476 path.setFillType(SkPath::kEvenOdd_FillType); 477 path.addCircle(center.fX, center.fY, maxSide + width / 2); 478 SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width, 479 (maxSide + width) * 2, (maxSide + width) * 2); 480 path.addRect(outside); 481 canvas->drawPath(path, paint); 482 } 483 484 void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) { 485 SkPaint paint; 486 paint.setAntiAlias(true); 487 paint.setStyle(SkPaint::kStroke_Style); 488 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); 489 canvas->drawRect(button.fBounds, paint); 490 paint.setTextSize(25.0f); 491 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); 492 paint.setTextAlign(SkPaint::kCenter_Align); 493 paint.setStyle(SkPaint::kFill_Style); 494 canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBounds.fBottom - 5, 495 paint); 496 } 497 498 void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value, 499 SkScalar min, SkScalar max, const char* name) { 500 SkPaint paint; 501 paint.setAntiAlias(true); 502 paint.setStyle(SkPaint::kStroke_Style); 503 canvas->drawRect(bounds, paint); 504 SkScalar scale = max - min; 505 SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale; 506 paint.setColor(0xFFFF0000); 507 canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint); 508 SkString label; 509 label.printf("%0.3g", value); 510 paint.setColor(0xFF000000); 511 paint.setTextSize(11.0f); 512 paint.setStyle(SkPaint::kFill_Style); 513 canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5, paint); 514 paint.setTextSize(13.0f); 515 canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint); 516 } 517 518 void setForGeometry() { 519 fDrawRibs = true; 520 fDrawTangents = true; 521 fDrawTDivs = false; 522 fWidthScale = 1; 523 } 524 525 void setForText() { 526 fDrawRibs = fDrawTangents = fDrawTDivs = false; 527 fWidthScale = 0.002f; 528 } 529 530 void setForSingles() { 531 setForGeometry(); 532 fDrawTDivs = true; 533 } 534 535 void setAsNeeded() { 536 if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled) { 537 setForSingles(); 538 } else if (fRRectButton.fEnabled || fCircleButton.fEnabled || fArcButton.fEnabled) { 539 setForGeometry(); 540 } else { 541 setForText(); 542 } 543 } 544 545 bool arcCenter(SkPoint* center) { 546 SkPath path; 547 path.moveTo(fPts[10]); 548 path.arcTo(fPts[11], fPts[12], fRadius); 549 SkPath::Iter iter(path, false); 550 SkPoint pts[4]; 551 iter.next(pts); 552 if (SkPath::kLine_Verb == iter.next(pts)) { 553 iter.next(pts); 554 } 555 SkVector before = pts[0] - pts[1]; 556 SkVector after = pts[1] - pts[2]; 557 before.setLength(fRadius); 558 after.setLength(fRadius); 559 SkVector beforeCCW, afterCCW; 560 before.rotateCCW(&beforeCCW); 561 after.rotateCCW(&afterCCW); 562 beforeCCW += pts[0]; 563 afterCCW += pts[2]; 564 *center = beforeCCW; 565 if (SkScalarNearlyEqual(beforeCCW.fX, afterCCW.fX) 566 && SkScalarNearlyEqual(beforeCCW.fY, afterCCW.fY)) { 567 return true; 568 } 569 SkVector beforeCW, afterCW; 570 before.rotateCW(&beforeCW); 571 after.rotateCW(&afterCW); 572 beforeCW += pts[0]; 573 afterCW += pts[2]; 574 *center = beforeCW; 575 return SkScalarNearlyEqual(beforeCW.fX, afterCW.fX) 576 && SkScalarNearlyEqual(beforeCCW.fY, afterCW.fY); 577 } 578 579 void onDrawContent(SkCanvas* canvas) override { 580 SkPath path; 581 SkScalar width = fWidth; 582 583 if (fCubicButton.fEnabled) { 584 path.moveTo(fPts[0]); 585 path.cubicTo(fPts[1], fPts[2], fPts[3]); 586 setForSingles(); 587 draw_stroke(canvas, path, width, 950, false); 588 } 589 590 if (fConicButton.fEnabled) { 591 path.reset(); 592 path.moveTo(fPts[4]); 593 path.conicTo(fPts[5], fPts[6], fWeight); 594 setForSingles(); 595 draw_stroke(canvas, path, width, 950, false); 596 } 597 598 if (fQuadButton.fEnabled) { 599 path.reset(); 600 path.moveTo(fPts[7]); 601 path.quadTo(fPts[8], fPts[9]); 602 setForSingles(); 603 draw_stroke(canvas, path, width, 950, false); 604 } 605 606 if (fArcButton.fEnabled) { 607 path.reset(); 608 path.moveTo(fPts[10]); 609 path.arcTo(fPts[11], fPts[12], fRadius); 610 setForGeometry(); 611 draw_stroke(canvas, path, width, 950, false); 612 SkPath pathPts; 613 pathPts.moveTo(fPts[10]); 614 pathPts.lineTo(fPts[11]); 615 pathPts.lineTo(fPts[12]); 616 draw_points(canvas, pathPts, SK_ColorDKGRAY, true); 617 } 618 619 if (fRRectButton.fEnabled) { 620 SkScalar rad = 32; 621 SkRect r; 622 r.set(&fPts[13], 2); 623 path.reset(); 624 SkRRect rr; 625 rr.setRectXY(r, rad, rad); 626 path.addRRect(rr); 627 setForGeometry(); 628 draw_stroke(canvas, path, width, 950, false); 629 630 path.reset(); 631 SkRRect rr2; 632 rr.inset(width/2, width/2, &rr2); 633 path.addRRect(rr2, SkPath::kCCW_Direction); 634 rr.inset(-width/2, -width/2, &rr2); 635 path.addRRect(rr2, SkPath::kCW_Direction); 636 SkPaint paint; 637 paint.setAntiAlias(true); 638 paint.setColor(0x40FF8844); 639 canvas->drawPath(path, paint); 640 } 641 642 if (fCircleButton.fEnabled) { 643 path.reset(); 644 SkRect r; 645 r.set(&fPts[15], 2); 646 path.addOval(r); 647 setForGeometry(); 648 if (fCircleButton.fFill) { 649 if (fArcButton.fEnabled) { 650 SkPoint center; 651 if (arcCenter(¢er)) { 652 r.set(center.fX - fRadius, center.fY - fRadius, center.fX + fRadius, 653 center.fY + fRadius); 654 } 655 } 656 draw_fill(canvas, r, width); 657 } else { 658 draw_stroke(canvas, path, width, 950, false); 659 } 660 } 661 662 if (fTextButton.fEnabled) { 663 path.reset(); 664 SkPaint paint; 665 paint.setAntiAlias(true); 666 paint.setTextSize(fTextSize); 667 paint.getTextPath(fText.c_str(), fText.size(), 0, fTextSize, &path); 668 setForText(); 669 draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true); 670 } 671 672 if (fAnimate) { 673 fWidth += fDWidth; 674 if (fDWidth > 0 && fWidth > kWidthMax) { 675 fDWidth = -fDWidth; 676 } else if (fDWidth < 0 && fWidth < kWidthMin) { 677 fDWidth = -fDWidth; 678 } 679 } 680 setAsNeeded(); 681 if (fConicButton.fEnabled) { 682 draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight"); 683 } 684 if (fArcButton.fEnabled) { 685 draw_control(canvas, fRadiusControl, fRadius, 0, 500, "radius"); 686 } 687 #ifdef SK_DEBUG 688 draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax, 689 "error"); 690 #endif 691 draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale, 692 kWidthMax * fWidthScale, "width"); 693 draw_button(canvas, fQuadButton); 694 draw_button(canvas, fCubicButton); 695 draw_button(canvas, fConicButton); 696 draw_button(canvas, fArcButton); 697 draw_button(canvas, fRRectButton); 698 draw_button(canvas, fCircleButton); 699 draw_button(canvas, fTextButton); 700 this->inval(nullptr); 701 } 702 703 class MyClick : public Click { 704 public: 705 int fIndex; 706 MyClick(SkView* target, int index) : Click(target), fIndex(index) {} 707 }; 708 709 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, 710 unsigned modi) override { 711 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { 712 if (hittest(fPts[i], x, y)) { 713 return new MyClick(this, (int)i); 714 } 715 } 716 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); 717 if (fWeightControl.contains(rectPt)) { 718 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); 719 } 720 if (fRadiusControl.contains(rectPt)) { 721 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2); 722 } 723 #ifdef SK_DEBUG 724 if (fErrorControl.contains(rectPt)) { 725 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); 726 } 727 #endif 728 if (fWidthControl.contains(rectPt)) { 729 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); 730 } 731 if (fCubicButton.fBounds.contains(rectPt)) { 732 fCubicButton.fEnabled ^= true; 733 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); 734 } 735 if (fConicButton.fBounds.contains(rectPt)) { 736 fConicButton.fEnabled ^= true; 737 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); 738 } 739 if (fQuadButton.fBounds.contains(rectPt)) { 740 fQuadButton.fEnabled ^= true; 741 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7); 742 } 743 if (fArcButton.fBounds.contains(rectPt)) { 744 fArcButton.fEnabled ^= true; 745 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8); 746 } 747 if (fRRectButton.fBounds.contains(rectPt)) { 748 fRRectButton.fEnabled ^= true; 749 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9); 750 } 751 if (fCircleButton.fBounds.contains(rectPt)) { 752 bool wasEnabled = fCircleButton.fEnabled; 753 fCircleButton.fEnabled = !fCircleButton.fFill; 754 fCircleButton.fFill = wasEnabled && !fCircleButton.fFill; 755 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 10); 756 } 757 if (fTextButton.fBounds.contains(rectPt)) { 758 fTextButton.fEnabled ^= true; 759 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 11); 760 } 761 return this->INHERITED::onFindClickHandler(x, y, modi); 762 } 763 764 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min, 765 SkScalar max) { 766 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min; 767 } 768 769 bool onClick(Click* click) override { 770 int index = ((MyClick*)click)->fIndex; 771 if (index < (int) SK_ARRAY_COUNT(fPts)) { 772 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX), 773 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY)); 774 this->inval(nullptr); 775 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) { 776 fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5); 777 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) { 778 fRadius = MapScreenYtoValue(click->fICurr.fY, fRadiusControl, 0, 500); 779 } 780 #ifdef SK_DEBUG 781 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) { 782 gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, 783 fErrorControl, kStrokerErrorMin, kStrokerErrorMax)); 784 gDebugStrokerErrorSet = true; 785 } 786 #endif 787 else if (index == (int) SK_ARRAY_COUNT(fPts) + 4) { 788 fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl, 789 kWidthMin, kWidthMax)); 790 fAnimate = fWidth <= kWidthMin; 791 } 792 return true; 793 } 794 795 private: 796 typedef SkView INHERITED; 797 }; 798 799 /////////////////////////////////////////////////////////////////////////////// 800 801 static SkView* F2() { return new QuadStrokerView; } 802 static SkViewRegister gR2(F2); 803