Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(423)

Side by Side Diff: samplecode/SampleQuadStroker.cpp

Issue 912273003: update sampleapp for stroking experiment (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix another number truncation error Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gyp/SampleApp.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "SkPathMeasure.h"
13 #include "SkRandom.h"
14 #include "SkRRect.h"
15 #include "SkColorPriv.h"
16 #include "SkStrokerPriv.h"
17 #include "SkSurface.h"
18
19 static 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
24 static 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
46 static 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
78 static void erase(SkSurface* surface) {
79 surface->getCanvas()->clear(SK_ColorTRANSPARENT);
80 }
81
82 struct StrokeTypeButton {
83 SkRect fBounds;
84 char fLabel;
85 bool fEnabled;
86 };
87
88 class QuadStrokerView : public SampleView {
89 enum {
90 SKELETON_COLOR = 0xFF0000FF,
91 WIREFRAME_COLOR = 0x80FF0000
92 };
93
94 enum {
95 kCount = 10
96 };
97 SkPoint fPts[kCount];
98 SkRect fErrorControl;
99 SkRect fWidthControl;
100 SkRect fBounds;
101 SkMatrix fMatrix, fInverse;
102 SkAutoTUnref<SkShader> fShader;
103 SkAutoTUnref<SkSurface> fMinSurface;
104 SkAutoTUnref<SkSurface> fMaxSurface;
105 StrokeTypeButton fCubicButton;
106 StrokeTypeButton fQuadButton;
107 StrokeTypeButton fRRectButton;
108 StrokeTypeButton fTextButton;
109 SkString fText;
110 SkScalar fTextSize;
111 SkScalar fWidth, fDWidth;
112 SkScalar fWidthScale;
113 int fW, fH, fZoom;
114 bool fAnimate;
115 bool fDrawRibs;
116 bool fDrawTangents;
117 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG)
118 #define kStrokerErrorMin 0.001f
119 #define kStrokerErrorMax 5
120 #endif
121 #define kWidthMin 1
122 #define kWidthMax 100
123 public:
124 QuadStrokerView() {
125 this->setBGColor(SK_ColorLTGRAY);
126
127 fPts[0].set(50, 200);
128 fPts[1].set(50, 100);
129 fPts[2].set(150, 50);
130 fPts[3].set(300, 50);
131
132 fPts[4].set(350, 200);
133 fPts[5].set(350, 100);
134 fPts[6].set(450, 50);
135
136 fPts[7].set(200, 200);
137 fPts[8].set(400, 400);
138
139 fPts[9].set(250, 800);
140 fText = "a";
141 fTextSize = 12;
142 fWidth = 50;
143 fDWidth = 0.25f;
144
145 fCubicButton.fLabel = 'C';
146 fCubicButton.fEnabled = false;
147 fQuadButton.fLabel = 'Q';
148 fQuadButton.fEnabled = false;
149 fRRectButton.fLabel = 'R';
150 fRRectButton.fEnabled = false;
151 fTextButton.fLabel = 'T';
152 fTextButton.fEnabled = true;
153 fAnimate = true;
154 setAsNeeded();
155 }
156
157 protected:
158 bool onQuery(SkEvent* evt) SK_OVERRIDE {
159 if (SampleCode::TitleQ(*evt)) {
160 SampleCode::TitleR(evt, "QuadStroker");
161 return true;
162 }
163 SkUnichar uni;
164 if (fTextButton.fEnabled && SampleCode::CharQ(*evt, &uni)) {
165 switch (uni) {
166 case ' ':
167 fText = "";
168 break;
169 case '-':
170 fTextSize = SkTMax(1.0f, fTextSize - 1);
171 break;
172 case '+':
173 case '=':
174 fTextSize += 1;
175 break;
176 default:
177 fText.appendUnichar(uni);
178 }
179 this->inval(NULL);
180 return true;
181 }
182 return this->INHERITED::onQuery(evt);
183 }
184
185 void onSizeChange() SK_OVERRIDE {
186 fErrorControl.setXYWH(this->width() - 100, 30, 30, 400);
187 fWidthControl.setXYWH(this->width() - 50, 30, 30, 400);
188 fCubicButton.fBounds.setXYWH(this->width() - 50, 450, 30, 30);
189 fQuadButton.fBounds.setXYWH(this->width() - 50, 500, 30, 30);
190 fRRectButton.fBounds.setXYWH(this->width() - 50, 550, 30, 30);
191 fTextButton.fBounds.setXYWH(this->width() - 50, 600, 30, 30);
192 this->INHERITED::onSizeChange();
193 }
194
195 void copyMinToMax() {
196 erase(fMaxSurface);
197 SkCanvas* canvas = fMaxSurface->getCanvas();
198 canvas->save();
199 canvas->concat(fMatrix);
200 fMinSurface->draw(canvas, 0, 0, NULL);
201 canvas->restore();
202
203 SkPaint paint;
204 paint.setXfermodeMode(SkXfermode::kClear_Mode);
205 for (int iy = 1; iy < fH; ++iy) {
206 SkScalar y = SkIntToScalar(iy * fZoom);
207 canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint );
208 }
209 for (int ix = 1; ix < fW; ++ix) {
210 SkScalar x = SkIntToScalar(ix * fZoom);
211 canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint );
212 }
213 }
214
215 void setWHZ(int width, int height, int zoom) {
216 fZoom = zoom;
217 fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zo om));
218 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
219 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
220 fShader.reset(sk_tool_utils::create_checkerboard_shader(
221 0xFFCCCCCC, 0xFFFFFFFF, zoom));
222
223 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
224 fMinSurface.reset(SkSurface::NewRaster(info));
225 info = info.makeWH(width * zoom, height * zoom);
226 fMaxSurface.reset(SkSurface::NewRaster(info));
227 }
228
229 void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color,
230 bool show_lines) {
231 SkPaint paint;
232 paint.setColor(color);
233 paint.setAlpha(0x80);
234 paint.setAntiAlias(true);
235 int n = path.countPoints();
236 SkAutoSTArray<32, SkPoint> pts(n);
237 if (show_lines && fDrawTangents) {
238 SkTArray<int> contourCounts;
239 getContourCounts(path, &contourCounts);
240 SkPoint* ptPtr = pts.get();
241 for (int i = 0; i < contourCounts.count(); ++i) {
242 int count = contourCounts[i];
243 path.getPoints(ptPtr, count);
244 canvas->drawPoints(SkCanvas::kPolygon_PointMode, count, ptPtr, p aint);
245 ptPtr += count;
246 }
247 } else {
248 n = getOnCurvePoints(path, pts.get());
249 }
250 paint.setStrokeWidth(5);
251 canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint);
252 }
253
254 void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width,
255 SkColor color) {
256 const SkScalar radius = width / 2;
257
258 SkPathMeasure meas(path, false);
259 SkScalar total = meas.getLength();
260
261 SkScalar delta = 8;
262 SkPaint paint;
263 paint.setColor(color);
264
265 SkPoint pos, tan;
266 for (SkScalar dist = 0; dist <= total; dist += delta) {
267 if (meas.getPosTan(dist, &pos, &tan)) {
268 tan.scale(radius);
269 tan.rotateCCW();
270 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(),
271 pos.x() - tan.x(), pos.y() - tan.y(), paint);
272 }
273 }
274 }
275
276 void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, bool drawText) {
277 SkRect bounds = path.getBounds();
278 if (bounds.isEmpty()) {
279 return;
280 }
281 this->setWHZ(SkScalarCeilToInt(bounds.right()), SkScalarRoundToInt(fText Size * 3 / 2),
282 SkScalarRoundToInt(950.0f / fTextSize));
283 erase(fMinSurface);
284 SkPaint paint;
285 paint.setColor(0x1f1f0f0f);
286 fMinSurface->getCanvas()->drawPath(path, paint);
287 paint.setStyle(SkPaint::kStroke_Style);
288 paint.setStrokeWidth(width * fTextSize * fTextSize);
289 paint.setColor(0x3f0f1f3f);
290 fMinSurface->getCanvas()->drawPath(path, paint);
291
292 this->copyMinToMax();
293 fMaxSurface->draw(canvas, 0, 0, NULL);
294
295 paint.setAntiAlias(true);
296 paint.setStyle(SkPaint::kStroke_Style);
297 paint.setStrokeWidth(1);
298
299 paint.setColor(SKELETON_COLOR);
300 SkPath scaled;
301 SkMatrix matrix;
302 matrix.reset();
303 matrix.setScale(950 / fTextSize, 950 / fTextSize);
304 if (drawText) {
305 path.transform(matrix, &scaled);
306 } else {
307 scaled = path;
308 }
309 canvas->drawPath(scaled, paint);
310 draw_points(canvas, scaled, SKELETON_COLOR, true);
311
312 if (fDrawRibs) {
313 draw_ribs(canvas, scaled, width, 0xFF00FF00);
314 }
315
316 SkPath fill;
317
318 SkPaint p;
319 p.setStyle(SkPaint::kStroke_Style);
320 p.setStrokeWidth(width * fTextSize * fTextSize);
321
322 p.getFillPath(path, &fill);
323 SkPath scaledFill;
324 if (drawText) {
325 fill.transform(matrix, &scaledFill);
326 } else {
327 scaledFill = fill;
328 }
329 paint.setColor(WIREFRAME_COLOR);
330 canvas->drawPath(scaledFill, paint);
331 draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
332 }
333
334 void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) {
335 SkPaint paint;
336 paint.setAntiAlias(true);
337 paint.setStyle(SkPaint::kStroke_Style);
338 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
339 canvas->drawRect(button.fBounds, paint);
340 paint.setTextSize(25.0f);
341 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000);
342 paint.setTextAlign(SkPaint::kCenter_Align);
343 paint.setStyle(SkPaint::kFill_Style);
344 canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBo unds.fBottom - 5,
345 paint);
346 }
347
348 void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value,
349 SkScalar min, SkScalar max, const char* name) {
350 SkPaint paint;
351 paint.setAntiAlias(true);
352 paint.setStyle(SkPaint::kStroke_Style);
353 canvas->drawRect(bounds, paint);
354 SkScalar scale = max - min;
355 SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale;
356 paint.setColor(0xFFFF0000);
357 canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint) ;
358 SkString label;
359 label.printf("%0.3g", value);
360 paint.setColor(0xFF000000);
361 paint.setTextSize(11.0f);
362 paint.setStyle(SkPaint::kFill_Style);
363 canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5 , paint);
364 paint.setTextSize(13.0f);
365 canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint);
366 }
367
368 void setForGeometry() {
369 fDrawRibs = true;
370 fDrawTangents = true;
371 fWidthScale = 1;
372 }
373
374 void setForText() {
375 fDrawRibs = fDrawTangents = false;
376 fWidthScale = 0.002f;
377 }
378
379 void setAsNeeded() {
380 if (fCubicButton.fEnabled || fQuadButton.fEnabled || fRRectButton.fEnabl ed) {
381 setForGeometry();
382 } else {
383 setForText();
384 }
385 }
386
387 void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
388 SkPath path;
389 SkScalar width = fWidth;
390
391 if (fCubicButton.fEnabled) {
392 path.moveTo(fPts[0]);
393 path.cubicTo(fPts[1], fPts[2], fPts[3]);
394 setForGeometry();
395 draw_stroke(canvas, path, width, false);
396 }
397
398 if (fQuadButton.fEnabled) {
399 path.reset();
400 path.moveTo(fPts[4]);
401 path.quadTo(fPts[5], fPts[6]);
402 setForGeometry();
403 draw_stroke(canvas, path, width, false);
404 }
405
406 if (fRRectButton.fEnabled) {
407 SkScalar rad = 32;
408 SkRect r;
409 r.set(&fPts[7], 2);
410 path.reset();
411 SkRRect rr;
412 rr.setRectXY(r, rad, rad);
413 path.addRRect(rr);
414 setForGeometry();
415 draw_stroke(canvas, path, width, false);
416
417 path.reset();
418 SkRRect rr2;
419 rr.inset(width/2, width/2, &rr2);
420 path.addRRect(rr2, SkPath::kCCW_Direction);
421 rr.inset(-width/2, -width/2, &rr2);
422 path.addRRect(rr2, SkPath::kCW_Direction);
423 SkPaint paint;
424 paint.setAntiAlias(true);
425 paint.setColor(0x40FF8844);
426 canvas->drawPath(path, paint);
427 }
428
429 if (fTextButton.fEnabled) {
430 path.reset();
431 SkPaint paint;
432 paint.setAntiAlias(true);
433 paint.setTextSize(fTextSize);
434 paint.getTextPath(fText.c_str(), fText.size(), 0, fTextSize, &path);
435 setForText();
436 draw_stroke(canvas, path, width * fWidthScale / fTextSize, true);
437 }
438
439 if (fAnimate) {
440 fWidth += fDWidth;
441 if (fDWidth > 0 && fWidth > kWidthMax) {
442 fDWidth = -fDWidth;
443 } else if (fDWidth < 0 && fWidth < kWidthMin) {
444 fDWidth = -fDWidth;
445 }
446 }
447 setAsNeeded();
448 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG)
449 draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin , kStrokerErrorMax,
450 "error");
451 #endif
452 draw_control(canvas, fWidthControl, fWidth * fWidthScale, kWidthMin * fW idthScale,
453 kWidthMax * fWidthScale, "width");
454 draw_button(canvas, fQuadButton);
455 draw_button(canvas, fCubicButton);
456 draw_button(canvas, fRRectButton);
457 draw_button(canvas, fTextButton);
458 this->inval(NULL);
459 }
460
461 class MyClick : public Click {
462 public:
463 int fIndex;
464 MyClick(SkView* target, int index) : Click(target), fIndex(index) {}
465 };
466
467 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
468 unsigned modi) SK_OVERRIDE {
469 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) {
470 if (hittest(fPts[i], x, y)) {
471 return new MyClick(this, (int)i);
472 }
473 }
474 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1);
475 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG)
476 if (fErrorControl.contains(rectPt)) {
477 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1);
478 }
479 #endif
480 if (fWidthControl.contains(rectPt)) {
481 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3);
482 }
483 if (fCubicButton.fBounds.contains(rectPt)) {
484 fCubicButton.fEnabled ^= true;
485 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4);
486 }
487 if (fQuadButton.fBounds.contains(rectPt)) {
488 fQuadButton.fEnabled ^= true;
489 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5);
490 }
491 if (fRRectButton.fBounds.contains(rectPt)) {
492 fRRectButton.fEnabled ^= true;
493 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6);
494 }
495 if (fTextButton.fBounds.contains(rectPt)) {
496 fTextButton.fEnabled ^= true;
497 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 7);
498 }
499 return this->INHERITED::onFindClickHandler(x, y, modi);
500 }
501
502 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min ,
503 SkScalar max) {
504 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min ) + min;
505 }
506
507 bool onClick(Click* click) SK_OVERRIDE {
508 int index = ((MyClick*)click)->fIndex;
509 if (index < (int) SK_ARRAY_COUNT(fPts)) {
510 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX ),
511 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY ));
512 this->inval(NULL);
513 }
514 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG)
515 else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) {
516 gDebugStrokerError = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fI Curr.fY,
517 fErrorControl, kStrokerErrorMin, kStrokerErrorMax));
518 gDebugStrokerErrorSet = true;
519 }
520 #endif
521 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) {
522 fWidth = SkTMax(FLT_EPSILON, MapScreenYtoValue(click->fICurr.fY, fWi dthControl,
523 kWidthMin, kWidthMax));
524 fAnimate = fWidth <= kWidthMin;
525 }
526 return true;
527 }
528
529 private:
530 typedef SkView INHERITED;
531 };
532
533 ///////////////////////////////////////////////////////////////////////////////
534
535 static SkView* F2() { return new QuadStrokerView; }
536 static SkViewRegister gR2(F2);
OLDNEW
« no previous file with comments | « gyp/SampleApp.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698