blob: 73373a2c979f415e14e573743c147e65f21b7372 [file] [log] [blame]
caryclark88c748a2015-02-18 10:56:00 -08001/*
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 "SkPathMeasure.h"
13#include "SkRandom.h"
14#include "SkRRect.h"
15#include "SkColorPriv.h"
16#include "SkStrokerPriv.h"
17#include "SkSurface.h"
18
19static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) {
20 const SkScalar TOL = 7;
21 return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL;
22}
23
24static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) {
25 SkPath::RawIter iter(path);
26 SkPoint pts[4];
27 SkPath::Verb verb;
28
29 int count = 0;
30 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
31 switch (verb) {
32 case SkPath::kMove_Verb:
33 case SkPath::kLine_Verb:
34 case SkPath::kQuad_Verb:
35 case SkPath::kConic_Verb:
36 case SkPath::kCubic_Verb:
37 storage[count++] = pts[0];
38 break;
39 default:
40 break;
41 }
42 }
43 return count;
44}
45
46static void getContourCounts(const SkPath& path, SkTArray<int>* contourCounts) {
47 SkPath::RawIter iter(path);
48 SkPoint pts[4];
49 SkPath::Verb verb;
50
51 int count = 0;
52 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
53 switch (verb) {
54 case SkPath::kMove_Verb:
55 case SkPath::kLine_Verb:
56 count += 1;
57 break;
58 case SkPath::kQuad_Verb:
59 case SkPath::kConic_Verb:
60 count += 2;
61 break;
62 case SkPath::kCubic_Verb:
63 count += 3;
64 break;
65 case SkPath::kClose_Verb:
66 contourCounts->push_back(count);
67 count = 0;
68 break;
69 default:
70 break;
71 }
72 }
73 if (count > 0) {
74 contourCounts->push_back(count);
75 }
76}
77
78static void erase(SkSurface* surface) {
79 surface->getCanvas()->clear(SK_ColorTRANSPARENT);
80}
81
82struct StrokeTypeButton {
83 SkRect fBounds;
84 char fLabel;
85 bool fEnabled;
86};
87
caryclark04e4d082015-02-20 06:33:57 -080088struct CircleTypeButton : public StrokeTypeButton {
89 bool fFill;
90};
91
caryclark88c748a2015-02-18 10:56:00 -080092class QuadStrokerView : public SampleView {
93 enum {
94 SKELETON_COLOR = 0xFF0000FF,
95 WIREFRAME_COLOR = 0x80FF0000
96 };
97
98 enum {
caryclark04e4d082015-02-20 06:33:57 -080099 kCount = 15
caryclark88c748a2015-02-18 10:56:00 -0800100 };
101 SkPoint fPts[kCount];
caryclark04e4d082015-02-20 06:33:57 -0800102 SkRect fWeightControl;
caryclark88c748a2015-02-18 10:56:00 -0800103 SkRect fErrorControl;
104 SkRect fWidthControl;
105 SkRect fBounds;
106 SkMatrix fMatrix, fInverse;
107 SkAutoTUnref<SkShader> fShader;
108 SkAutoTUnref<SkSurface> fMinSurface;
109 SkAutoTUnref<SkSurface> fMaxSurface;
110 StrokeTypeButton fCubicButton;
caryclark04e4d082015-02-20 06:33:57 -0800111 StrokeTypeButton fConicButton;
caryclark88c748a2015-02-18 10:56:00 -0800112 StrokeTypeButton fQuadButton;
113 StrokeTypeButton fRRectButton;
caryclark04e4d082015-02-20 06:33:57 -0800114 CircleTypeButton fCircleButton;
caryclark88c748a2015-02-18 10:56:00 -0800115 StrokeTypeButton fTextButton;
116 SkString fText;
117 SkScalar fTextSize;
caryclark04e4d082015-02-20 06:33:57 -0800118 SkScalar fWeight;
caryclark88c748a2015-02-18 10:56:00 -0800119 SkScalar fWidth, fDWidth;
120 SkScalar fWidthScale;
121 int fW, fH, fZoom;
122 bool fAnimate;
123 bool fDrawRibs;
124 bool fDrawTangents;
caryclark1297ecf2015-02-24 06:02:06 -0800125#if !defined SK_LEGACY_STROKE_CURVES && defined(SK_DEBUG)
caryclark88c748a2015-02-18 10:56:00 -0800126 #define kStrokerErrorMin 0.001f
127 #define kStrokerErrorMax 5
128#endif
129 #define kWidthMin 1
130 #define kWidthMax 100
131public:
132 QuadStrokerView() {
133 this->setBGColor(SK_ColorLTGRAY);
134
caryclark04e4d082015-02-20 06:33:57 -0800135 fPts[0].set(50, 200); // cubic
caryclark88c748a2015-02-18 10:56:00 -0800136 fPts[1].set(50, 100);
137 fPts[2].set(150, 50);
138 fPts[3].set(300, 50);
139
caryclark04e4d082015-02-20 06:33:57 -0800140 fPts[4].set(350, 200); // conic
caryclark88c748a2015-02-18 10:56:00 -0800141 fPts[5].set(350, 100);
142 fPts[6].set(450, 50);
143
caryclark04e4d082015-02-20 06:33:57 -0800144 fPts[7].set(150, 300); // quad
145 fPts[8].set(150, 200);
146 fPts[9].set(250, 150);
caryclark88c748a2015-02-18 10:56:00 -0800147
caryclark04e4d082015-02-20 06:33:57 -0800148 fPts[10].set(200, 200); // rrect
149 fPts[11].set(400, 400);
150
151 fPts[12].set(250, 250); // oval
152 fPts[13].set(450, 450);
153
caryclark88c748a2015-02-18 10:56:00 -0800154 fText = "a";
155 fTextSize = 12;
156 fWidth = 50;
157 fDWidth = 0.25f;
caryclark04e4d082015-02-20 06:33:57 -0800158 fWeight = 1;
caryclark88c748a2015-02-18 10:56:00 -0800159
160 fCubicButton.fLabel = 'C';
161 fCubicButton.fEnabled = false;
caryclark04e4d082015-02-20 06:33:57 -0800162 fConicButton.fLabel = 'K';
163 fConicButton.fEnabled = true;
caryclark88c748a2015-02-18 10:56:00 -0800164 fQuadButton.fLabel = 'Q';
165 fQuadButton.fEnabled = false;
166 fRRectButton.fLabel = 'R';
167 fRRectButton.fEnabled = false;
caryclark04e4d082015-02-20 06:33:57 -0800168 fCircleButton.fLabel = 'O';
169 fCircleButton.fEnabled = false;
170 fCircleButton.fFill = false;
caryclark88c748a2015-02-18 10:56:00 -0800171 fTextButton.fLabel = 'T';
caryclark04e4d082015-02-20 06:33:57 -0800172 fTextButton.fEnabled = false;
caryclark88c748a2015-02-18 10:56:00 -0800173 fAnimate = true;
174 setAsNeeded();
175 }
176
177protected:
mtklein36352bf2015-03-25 18:17:31 -0700178 bool onQuery(SkEvent* evt) override {
caryclark88c748a2015-02-18 10:56:00 -0800179 if (SampleCode::TitleQ(*evt)) {
180 SampleCode::TitleR(evt, "QuadStroker");
181 return true;
182 }
183 SkUnichar uni;
184 if (fTextButton.fEnabled && SampleCode::CharQ(*evt, &uni)) {
185 switch (uni) {
186 case ' ':
187 fText = "";
188 break;
189 case '-':
190 fTextSize = SkTMax(1.0f, fTextSize - 1);
191 break;
192 case '+':
193 case '=':
194 fTextSize += 1;
195 break;
196 default:
197 fText.appendUnichar(uni);
198 }
199 this->inval(NULL);
200 return true;
201 }
202 return this->INHERITED::onQuery(evt);
203 }
204
mtklein36352bf2015-03-25 18:17:31 -0700205 void onSizeChange() override {
caryclark04e4d082015-02-20 06:33:57 -0800206 fWeightControl.setXYWH(this->width() - 150, 30, 30, 400);
caryclark88c748a2015-02-18 10:56:00 -0800207 fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
208 fWidthControl.setXYWH(this->width() - 50, 30, 30, 400);
caryclark04e4d082015-02-20 06:33:57 -0800209 int buttonOffset = 450;
210 fCubicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
211 buttonOffset += 50;
212 fConicButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
213 buttonOffset += 50;
214 fQuadButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
215 buttonOffset += 50;
216 fRRectButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
217 buttonOffset += 50;
218 fCircleButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
219 buttonOffset += 50;
220 fTextButton.fBounds.setXYWH(this->width() - 50, SkIntToScalar(buttonOffset), 30, 30);
caryclark88c748a2015-02-18 10:56:00 -0800221 this->INHERITED::onSizeChange();
222 }
223
224 void copyMinToMax() {
225 erase(fMaxSurface);
226 SkCanvas* canvas = fMaxSurface->getCanvas();
227 canvas->save();
228 canvas->concat(fMatrix);
229 fMinSurface->draw(canvas, 0, 0, NULL);
230 canvas->restore();
231
232 SkPaint paint;
233 paint.setXfermodeMode(SkXfermode::kClear_Mode);
234 for (int iy = 1; iy < fH; ++iy) {
235 SkScalar y = SkIntToScalar(iy * fZoom);
236 canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
237 }
238 for (int ix = 1; ix < fW; ++ix) {
239 SkScalar x = SkIntToScalar(ix * fZoom);
240 canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
241 }
242 }
243
244 void setWHZ(int width, int height, int zoom) {
245 fZoom = zoom;
246 fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
247 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
248 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
249 fShader.reset(sk_tool_utils::create_checkerboard_shader(
250 0xFFCCCCCC, 0xFFFFFFFF, zoom));
251
252 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
253 fMinSurface.reset(SkSurface::NewRaster(info));
254 info = info.makeWH(width * zoom, height * zoom);
255 fMaxSurface.reset(SkSurface::NewRaster(info));
256 }
257
258 void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
259 bool show_lines) {
260 SkPaint paint;
261 paint.setColor(color);
262 paint.setAlpha(0x80);
263 paint.setAntiAlias(true);
264 int n = path.countPoints();
265 SkAutoSTArray<32, SkPoint> pts(n);
266 if (show_lines && fDrawTangents) {
267 SkTArray<int> contourCounts;
268 getContourCounts(path, &contourCounts);
269 SkPoint* ptPtr = pts.get();
270 for (int i = 0; i < contourCounts.count(); ++i) {
271 int count = contourCounts[i];
272 path.getPoints(ptPtr, count);
273 canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, paint);
274 ptPtr += count;
275 }
276 } else {
277 n = getOnCurvePoints(path, pts.get());
278 }
279 paint.setStrokeWidth(5);
280 canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
281 }
282
283 void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
284 SkColor color) {
285 const SkScalar radius = width / 2;
286
287 SkPathMeasure meas(path, false);
288 SkScalar total = meas.getLength();
289
290 SkScalar delta = 8;
291 SkPaint paint;
292 paint.setColor(color);
293
294 SkPoint pos, tan;
295 for (SkScalar dist = 0; dist <= total; dist += delta) {
296 if (meas.getPosTan(dist, &pos, &tan)) {
297 tan.scale(radius);
298 tan.rotateCCW();
299 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
300 pos.x() - tan.x(), pos.y() - tan.y(), paint);
301 }
302 }
303 }
304
caryclark04e4d082015-02-20 06:33:57 -0800305 void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
306 bool drawText) {
caryclark88c748a2015-02-18 10:56:00 -0800307 SkRect bounds = path.getBounds();
308 if (bounds.isEmpty()) {
309 return;
310 }
caryclark04e4d082015-02-20 06:33:57 -0800311 this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText
312 ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
313 SkScalarRoundToInt(950.0f / scale));
caryclark88c748a2015-02-18 10:56:00 -0800314 erase(fMinSurface);
315 SkPaint paint;
316 paint.setColor(0x1f1f0f0f);
caryclark88c748a2015-02-18 10:56:00 -0800317 paint.setStyle(SkPaint::kStroke_Style);
caryclark04e4d082015-02-20 06:33:57 -0800318 paint.setStrokeWidth(width * scale * scale);
caryclark88c748a2015-02-18 10:56:00 -0800319 paint.setColor(0x3f0f1f3f);
caryclark04e4d082015-02-20 06:33:57 -0800320 if (drawText) {
321 fMinSurface->getCanvas()->drawPath(path, paint);
322 this->copyMinToMax();
323 fMaxSurface->draw(canvas, 0, 0, NULL);
324 }
caryclark88c748a2015-02-18 10:56:00 -0800325 paint.setAntiAlias(true);
326 paint.setStyle(SkPaint::kStroke_Style);
327 paint.setStrokeWidth(1);
328
329 paint.setColor(SKELETON_COLOR);
330 SkPath scaled;
331 SkMatrix matrix;
332 matrix.reset();
caryclark04e4d082015-02-20 06:33:57 -0800333 matrix.setScale(950 / scale, 950 / scale);
caryclark88c748a2015-02-18 10:56:00 -0800334 if (drawText) {
335 path.transform(matrix, &scaled);
336 } else {
337 scaled = path;
338 }
339 canvas->drawPath(scaled, paint);
340 draw_points(canvas, scaled, SKELETON_COLOR, true);
341
342 if (fDrawRibs) {
343 draw_ribs(canvas, scaled, width, 0xFF00FF00);
344 }
345
346 SkPath fill;
347
348 SkPaint p;
349 p.setStyle(SkPaint::kStroke_Style);
caryclark04e4d082015-02-20 06:33:57 -0800350 if (drawText) {
351 p.setStrokeWidth(width * scale * scale);
352 } else {
353 p.setStrokeWidth(width);
354 }
caryclark88c748a2015-02-18 10:56:00 -0800355 p.getFillPath(path, &fill);
356 SkPath scaledFill;
357 if (drawText) {
358 fill.transform(matrix, &scaledFill);
359 } else {
360 scaledFill = fill;
361 }
362 paint.setColor(WIREFRAME_COLOR);
363 canvas->drawPath(scaledFill, paint);
364 draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
365 }
366
caryclark04e4d082015-02-20 06:33:57 -0800367 void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) {
368 if (rect.isEmpty()) {
369 return;
370 }
371 SkPaint paint;
372 paint.setColor(0x1f1f0f0f);
373 paint.setStyle(SkPaint::kStroke_Style);
374 paint.setStrokeWidth(width);
375 SkPath path;
376 SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2;
377 SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide };
378 path.addCircle(center.fX, center.fY, maxSide);
379 canvas->drawPath(path, paint);
380 paint.setStyle(SkPaint::kFill_Style);
381 path.reset();
382 path.addCircle(center.fX, center.fY, maxSide - width / 2);
383 paint.setColor(0x3f0f1f3f);
384 canvas->drawPath(path, paint);
385 path.reset();
386 path.setFillType(SkPath::kEvenOdd_FillType);
387 path.addCircle(center.fX, center.fY, maxSide + width / 2);
388 SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width,
389 (maxSide + width) * 2, (maxSide + width) * 2);
390 path.addRect(outside);
391 canvas->drawPath(path, paint);
392 }
393
caryclark88c748a2015-02-18 10:56:00 -0800394 void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) {
395 SkPaint paint;
396 paint.setAntiAlias(true);
397 paint.setStyle(SkPaint::kStroke_Style);
398 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
399 canvas->drawRect(button.fBounds, paint);
400 paint.setTextSize(25.0f);
401 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
402 paint.setTextAlign(SkPaint::kCenter_Align);
403 paint.setStyle(SkPaint::kFill_Style);
404 canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBounds.fBottom - 5,
405 paint);
406 }
407
408 void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value,
409 SkScalar min, SkScalar max, const char* name) {
410 SkPaint paint;
411 paint.setAntiAlias(true);
412 paint.setStyle(SkPaint::kStroke_Style);
413 canvas->drawRect(bounds, paint);
414 SkScalar scale = max - min;
415 SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale;
416 paint.setColor(0xFFFF0000);
417 canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint);
418 SkString label;
419 label.printf("%0.3g", value);
420 paint.setColor(0xFF000000);
421 paint.setTextSize(11.0f);
422 paint.setStyle(SkPaint::kFill_Style);
423 canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5, paint);
424 paint.setTextSize(13.0f);
425 canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint);
426 }
427
428 void setForGeometry() {
429 fDrawRibs = true;
430 fDrawTangents = true;
431 fWidthScale = 1;
432 }
433
434 void setForText() {
435 fDrawRibs = fDrawTangents = false;
436 fWidthScale = 0.002f;
437 }
438
439 void setAsNeeded() {
caryclark04e4d082015-02-20 06:33:57 -0800440 if (fConicButton.fEnabled || fCubicButton.fEnabled || fQuadButton.fEnabled
441 || fRRectButton.fEnabled || fCircleButton.fEnabled) {
caryclark88c748a2015-02-18 10:56:00 -0800442 setForGeometry();
443 } else {
444 setForText();
445 }
446 }
447
mtklein36352bf2015-03-25 18:17:31 -0700448 void onDrawContent(SkCanvas* canvas) override {
caryclark88c748a2015-02-18 10:56:00 -0800449 SkPath path;
450 SkScalar width = fWidth;
451
452 if (fCubicButton.fEnabled) {
453 path.moveTo(fPts[0]);
454 path.cubicTo(fPts[1], fPts[2], fPts[3]);
455 setForGeometry();
caryclark04e4d082015-02-20 06:33:57 -0800456 draw_stroke(canvas, path, width, 950, false);
457 }
458
459 if (fConicButton.fEnabled) {
460 path.moveTo(fPts[4]);
461 path.conicTo(fPts[5], fPts[6], fWeight);
462 setForGeometry();
463 draw_stroke(canvas, path, width, 950, false);
caryclark88c748a2015-02-18 10:56:00 -0800464 }
465
466 if (fQuadButton.fEnabled) {
467 path.reset();
caryclark04e4d082015-02-20 06:33:57 -0800468 path.moveTo(fPts[7]);
469 path.quadTo(fPts[8], fPts[9]);
caryclark88c748a2015-02-18 10:56:00 -0800470 setForGeometry();
caryclark04e4d082015-02-20 06:33:57 -0800471 draw_stroke(canvas, path, width, 950, false);
caryclark88c748a2015-02-18 10:56:00 -0800472 }
473
474 if (fRRectButton.fEnabled) {
475 SkScalar rad = 32;
476 SkRect r;
caryclark04e4d082015-02-20 06:33:57 -0800477 r.set(&fPts[10], 2);
caryclark88c748a2015-02-18 10:56:00 -0800478 path.reset();
479 SkRRect rr;
480 rr.setRectXY(r, rad, rad);
481 path.addRRect(rr);
482 setForGeometry();
caryclark04e4d082015-02-20 06:33:57 -0800483 draw_stroke(canvas, path, width, 950, false);
caryclark88c748a2015-02-18 10:56:00 -0800484
485 path.reset();
486 SkRRect rr2;
487 rr.inset(width/2, width/2, &rr2);
488 path.addRRect(rr2, SkPath::kCCW_Direction);
489 rr.inset(-width/2, -width/2, &rr2);
490 path.addRRect(rr2, SkPath::kCW_Direction);
491 SkPaint paint;
492 paint.setAntiAlias(true);
493 paint.setColor(0x40FF8844);
494 canvas->drawPath(path, paint);
495 }
496
caryclark04e4d082015-02-20 06:33:57 -0800497 if (fCircleButton.fEnabled) {
498 path.reset();
499 SkRect r;
500 r.set(&fPts[12], 2);
501 path.addOval(r);
502 setForGeometry();
503 if (fCircleButton.fFill) {
504 draw_fill(canvas, r, width);
505 } else {
506 draw_stroke(canvas, path, width, 950, false);
507 }
508 }
509
caryclark88c748a2015-02-18 10:56:00 -0800510 if (fTextButton.fEnabled) {
511 path.reset();
512 SkPaint paint;
513 paint.setAntiAlias(true);
514 paint.setTextSize(fTextSize);
515 paint.getTextPath(fText.c_str(), fText.size(), 0, fTextSize, &path);
516 setForText();
caryclark04e4d082015-02-20 06:33:57 -0800517 draw_stroke(canvas, path, width * fWidthScale / fTextSize, fTextSize, true);
caryclark88c748a2015-02-18 10:56:00 -0800518 }
519
520 if (fAnimate) {
521 fWidth += fDWidth;
522 if (fDWidth > 0 && fWidth > kWidthMax) {
523 fDWidth = -fDWidth;
524 } else if (fDWidth < 0 && fWidth < kWidthMin) {
525 fDWidth = -fDWidth;
526 }
527 }
528 setAsNeeded();
caryclark04e4d082015-02-20 06:33:57 -0800529 if (fConicButton.fEnabled) {
530 draw_control(canvas, fWeightControl, fWeight, 0, 5, "weight");
531 }
caryclark1297ecf2015-02-24 06:02:06 -0800532#if !defined SK_LEGACY_STROKE_CURVES && defined(SK_DEBUG)
caryclark88c748a2015-02-18 10:56:00 -0800533 draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax,
534 "error");
535#endif
536 draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fWidthScale,
537 kWidthMax * fWidthScale, "width");
538 draw_button(canvas, fQuadButton);
539 draw_button(canvas, fCubicButton);
caryclark04e4d082015-02-20 06:33:57 -0800540 draw_button(canvas, fConicButton);
caryclark88c748a2015-02-18 10:56:00 -0800541 draw_button(canvas, fRRectButton);
caryclark04e4d082015-02-20 06:33:57 -0800542 draw_button(canvas, fCircleButton);
caryclark88c748a2015-02-18 10:56:00 -0800543 draw_button(canvas, fTextButton);
544 this->inval(NULL);
545 }
546
547 class MyClick : public Click {
548 public:
549 int fIndex;
550 MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
551 };
552
553 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
mtklein36352bf2015-03-25 18:17:31 -0700554 unsigned modi) override {
caryclark88c748a2015-02-18 10:56:00 -0800555 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
556 if (hittest(fPts[i], x, y)) {
557 return new MyClick(this, (int)i);
558 }
559 }
560 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
caryclark04e4d082015-02-20 06:33:57 -0800561 if (fWeightControl.contains(rectPt)) {
562 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1);
563 }
caryclark1297ecf2015-02-24 06:02:06 -0800564#if !defined SK_LEGACY_STROKE_CURVES && defined(SK_DEBUG)
caryclark88c748a2015-02-18 10:56:00 -0800565 if (fErrorControl.contains(rectPt)) {
caryclark04e4d082015-02-20 06:33:57 -0800566 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 2);
caryclark88c748a2015-02-18 10:56:00 -0800567 }
568#endif
569 if (fWidthControl.contains(rectPt)) {
570 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3);
571 }
572 if (fCubicButton.fBounds.contains(rectPt)) {
573 fCubicButton.fEnabled ^= true;
574 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4);
575 }
caryclark04e4d082015-02-20 06:33:57 -0800576 if (fConicButton.fBounds.contains(rectPt)) {
577 fConicButton.fEnabled ^= true;
578 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5);
579 }
caryclark88c748a2015-02-18 10:56:00 -0800580 if (fQuadButton.fBounds.contains(rectPt)) {
581 fQuadButton.fEnabled ^= true;
caryclark04e4d082015-02-20 06:33:57 -0800582 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6);
caryclark88c748a2015-02-18 10:56:00 -0800583 }
584 if (fRRectButton.fBounds.contains(rectPt)) {
585 fRRectButton.fEnabled ^= true;
caryclark04e4d082015-02-20 06:33:57 -0800586 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7);
587 }
588 if (fCircleButton.fBounds.contains(rectPt)) {
589 bool wasEnabled = fCircleButton.fEnabled;
590 fCircleButton.fEnabled = !fCircleButton.fFill;
591 fCircleButton.fFill = wasEnabled && !fCircleButton.fFill;
592 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 8);
caryclark88c748a2015-02-18 10:56:00 -0800593 }
594 if (fTextButton.fBounds.contains(rectPt)) {
595 fTextButton.fEnabled ^= true;
caryclark04e4d082015-02-20 06:33:57 -0800596 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 9);
caryclark88c748a2015-02-18 10:56:00 -0800597 }
598 return this->INHERITED::onFindClickHandler(x, y, modi);
599 }
600
601 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min,
602 SkScalar max) {
603 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min;
604 }
605
mtklein36352bf2015-03-25 18:17:31 -0700606 bool onClick(Click* click) override {
caryclark88c748a2015-02-18 10:56:00 -0800607 int index = ((MyClick*)click)->fIndex;
608 if (index < (int) SK_ARRAY_COUNT(fPts)) {
609 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX),
610 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY));
611 this->inval(NULL);
caryclark04e4d082015-02-20 06:33:57 -0800612 } else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
613 fWeight = MapScreenYtoValue(click->fICurr.fY, fWeightControl, 0, 5);
caryclark88c748a2015-02-18 10:56:00 -0800614 }
caryclark1297ecf2015-02-24 06:02:06 -0800615#if !defined SK_LEGACY_STROKE_CURVES && defined(SK_DEBUG)
caryclark04e4d082015-02-20 06:33:57 -0800616 else if (index == (int) SK_ARRAY_COUNT(fPts) + 2) {
caryclark88c748a2015-02-18 10:56:00 -0800617 gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY,
618 fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
619 gDebugStrokerErrorSet = true;
620 }
621#endif
622 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
623 fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWidthControl,
624 kWidthMin, kWidthMax));
625 fAnimate = fWidth <= kWidthMin;
626 }
627 return true;
628 }
629
630private:
631 typedef SkView INHERITED;
632};
633
634///////////////////////////////////////////////////////////////////////////////
635
636static SkView* F2() { return new QuadStrokerView; }
637static SkViewRegister gR2(F2);