| OLD | NEW |
| (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 "SampleCode.h" | |
| 9 #include "SkView.h" | |
| 10 #include "SkCanvas.h" | |
| 11 #include "SkRandom.h" | |
| 12 #include "SkRRect.h" | |
| 13 #include "SkColorPriv.h" | |
| 14 #include "SkStrokerPriv.h" | |
| 15 | |
| 16 static void rotateAbout(SkCanvas* canvas, SkScalar degrees, | |
| 17 SkScalar cx, SkScalar cy) { | |
| 18 canvas->translate(cx, cy); | |
| 19 canvas->rotate(degrees); | |
| 20 canvas->translate(-cx, -cy); | |
| 21 } | |
| 22 | |
| 23 class RotateCirclesView : public SampleView { | |
| 24 public: | |
| 25 RotateCirclesView() { | |
| 26 this->setBGColor(SK_ColorLTGRAY); | |
| 27 | |
| 28 fAngle = 0; | |
| 29 } | |
| 30 | |
| 31 protected: | |
| 32 // overrides from SkEventSink | |
| 33 virtual bool onQuery(SkEvent* evt) { | |
| 34 if (SampleCode::TitleQ(*evt)) { | |
| 35 SampleCode::TitleR(evt, "RotateCircles"); | |
| 36 return true; | |
| 37 } | |
| 38 return this->INHERITED::onQuery(evt); | |
| 39 } | |
| 40 | |
| 41 virtual void onDrawContent(SkCanvas* canvas) { | |
| 42 SkRandom rand; | |
| 43 SkPaint paint; | |
| 44 paint.setAntiAlias(true); | |
| 45 paint.setStrokeWidth(20); | |
| 46 | |
| 47 SkScalar cx = 240; | |
| 48 SkScalar cy = 240; | |
| 49 SkScalar DX = 240 * 2; | |
| 50 SkColor color = 0; | |
| 51 | |
| 52 float scale = 1; | |
| 53 float sign = 0.3f; | |
| 54 for (SkScalar rad = 200; rad >= 20; rad -= 15) { | |
| 55 sign = -sign; | |
| 56 scale += 0.2f; | |
| 57 | |
| 58 paint.setColor(rand.nextU()); | |
| 59 paint.setAlpha(0xFF); | |
| 60 color = ~color; | |
| 61 | |
| 62 paint.setStyle(SkPaint::kFill_Style); | |
| 63 | |
| 64 canvas->save(); | |
| 65 rotateAbout(canvas, fAngle * scale * sign, cx, cy); | |
| 66 canvas->drawCircle(cx, cy, rad, paint); | |
| 67 canvas->restore(); | |
| 68 | |
| 69 paint.setStyle(SkPaint::kStroke_Style); | |
| 70 paint.setStrokeWidth(rad*2); | |
| 71 | |
| 72 canvas->save(); | |
| 73 rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy); | |
| 74 canvas->drawCircle(cx + DX, cy, 10, paint); | |
| 75 canvas->restore(); | |
| 76 | |
| 77 canvas->save(); | |
| 78 rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy + DX); | |
| 79 canvas->drawCircle(cx + DX, cy + DX, 10, paint); | |
| 80 canvas->restore(); | |
| 81 | |
| 82 } | |
| 83 | |
| 84 fAngle = (fAngle + 1) % 360; | |
| 85 this->inval(NULL); | |
| 86 } | |
| 87 | |
| 88 private: | |
| 89 int fAngle; | |
| 90 typedef SkView INHERITED; | |
| 91 }; | |
| 92 | |
| 93 class TestCirclesView : public SampleView { | |
| 94 public: | |
| 95 TestCirclesView() { | |
| 96 } | |
| 97 | |
| 98 protected: | |
| 99 bool onQuery(SkEvent* evt) SK_OVERRIDE { | |
| 100 if (SampleCode::TitleQ(*evt)) { | |
| 101 SampleCode::TitleR(evt, "RotateCircles2"); | |
| 102 return true; | |
| 103 } | |
| 104 return this->INHERITED::onQuery(evt); | |
| 105 } | |
| 106 | |
| 107 void draw_real_circle(SkCanvas* canvas, SkScalar radius) { | |
| 108 int w = SkScalarCeilToInt(radius * 2); | |
| 109 int h = w; | |
| 110 | |
| 111 SkBitmap bm; | |
| 112 bm.allocN32Pixels(w, h); | |
| 113 bm.eraseColor(0); | |
| 114 | |
| 115 SkAutoLockPixels alp(bm); | |
| 116 | |
| 117 SkScalar cx = radius; | |
| 118 SkScalar cy = radius; | |
| 119 for (int y = 0; y < h; y += 1) { | |
| 120 for (int x = 0; x < w; x += 1) { | |
| 121 float d = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy)); | |
| 122 if (d <= radius) { | |
| 123 *bm.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 canvas->drawBitmap(bm, 0, 0, NULL); | |
| 129 } | |
| 130 | |
| 131 void onDrawContent(SkCanvas* canvas) SK_OVERRIDE { | |
| 132 SkScalar radius = 256; | |
| 133 canvas->translate(10, 10); | |
| 134 | |
| 135 draw_real_circle(canvas, radius); | |
| 136 | |
| 137 SkPaint paint; | |
| 138 paint.setAntiAlias(true); | |
| 139 | |
| 140 paint.setColor(0x80FF0000); | |
| 141 canvas->drawCircle(radius, radius, radius, paint); | |
| 142 | |
| 143 paint.setStyle(SkPaint::kStroke_Style); | |
| 144 paint.setStrokeWidth(radius); | |
| 145 paint.setColor(0x8000FF00); | |
| 146 canvas->drawCircle(radius, radius, radius/2, paint); | |
| 147 } | |
| 148 | |
| 149 private: | |
| 150 typedef SkView INHERITED; | |
| 151 }; | |
| 152 | |
| 153 static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) { | |
| 154 const SkScalar TOL = 7; | |
| 155 return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL; | |
| 156 } | |
| 157 | |
| 158 static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) { | |
| 159 SkPath::RawIter iter(path); | |
| 160 SkPoint pts[4]; | |
| 161 SkPath::Verb verb; | |
| 162 | |
| 163 int count = 0; | |
| 164 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { | |
| 165 switch (verb) { | |
| 166 case SkPath::kMove_Verb: | |
| 167 case SkPath::kLine_Verb: | |
| 168 case SkPath::kQuad_Verb: | |
| 169 case SkPath::kCubic_Verb: | |
| 170 storage[count++] = pts[0]; | |
| 171 break; | |
| 172 default: | |
| 173 break; | |
| 174 } | |
| 175 } | |
| 176 return count; | |
| 177 } | |
| 178 | |
| 179 #include "SkPathMeasure.h" | |
| 180 | |
| 181 struct StrokeTypeButton { | |
| 182 SkRect fBounds; | |
| 183 char fLabel; | |
| 184 bool fEnabled; | |
| 185 }; | |
| 186 | |
| 187 class TestStrokeView : public SampleView { | |
| 188 enum { | |
| 189 SKELETON_COLOR = 0xFF0000FF, | |
| 190 WIREFRAME_COLOR = 0x80FF0000 | |
| 191 }; | |
| 192 | |
| 193 enum { | |
| 194 kCount = 9 | |
| 195 }; | |
| 196 SkPoint fPts[kCount]; | |
| 197 SkRect fErrorControl; | |
| 198 SkRect fWidthControl; | |
| 199 StrokeTypeButton fCubicButton; | |
| 200 StrokeTypeButton fQuadButton; | |
| 201 StrokeTypeButton fRRectButton; | |
| 202 SkScalar fWidth, fDWidth; | |
| 203 bool fAnimate; | |
| 204 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) | |
| 205 #define kStrokerErrorMin 0.001f | |
| 206 #define kStrokerErrorMax 5 | |
| 207 #endif | |
| 208 #define kWidthMin 1 | |
| 209 #define kWidthMax 100 | |
| 210 public: | |
| 211 TestStrokeView() { | |
| 212 this->setBGColor(SK_ColorLTGRAY); | |
| 213 | |
| 214 fPts[0].set(50, 200); | |
| 215 fPts[1].set(50, 100); | |
| 216 fPts[2].set(150, 50); | |
| 217 fPts[3].set(300, 50); | |
| 218 | |
| 219 fPts[4].set(350, 200); | |
| 220 fPts[5].set(350, 100); | |
| 221 fPts[6].set(450, 50); | |
| 222 | |
| 223 fPts[7].set(200, 200); | |
| 224 fPts[8].set(400, 400); | |
| 225 | |
| 226 fWidth = 50; | |
| 227 fDWidth = 0.25f; | |
| 228 | |
| 229 fCubicButton.fLabel = 'C'; | |
| 230 fCubicButton.fEnabled = true; | |
| 231 fQuadButton.fLabel = 'Q'; | |
| 232 fQuadButton.fEnabled = true; | |
| 233 fRRectButton.fLabel = 'R'; | |
| 234 fRRectButton.fEnabled = true; | |
| 235 fAnimate = true; | |
| 236 } | |
| 237 | |
| 238 protected: | |
| 239 bool onQuery(SkEvent* evt) SK_OVERRIDE { | |
| 240 if (SampleCode::TitleQ(*evt)) { | |
| 241 SampleCode::TitleR(evt, "RotateCircles3"); | |
| 242 return true; | |
| 243 } | |
| 244 return this->INHERITED::onQuery(evt); | |
| 245 } | |
| 246 | |
| 247 void onSizeChange() SK_OVERRIDE { | |
| 248 fErrorControl.setXYWH(this->width() - 100, 30, 30, 400); | |
| 249 fWidthControl.setXYWH(this->width() - 50, 30, 30, 400); | |
| 250 fCubicButton.fBounds.setXYWH(this->width() - 50, 450, 30, 30); | |
| 251 fQuadButton.fBounds.setXYWH(this->width() - 50, 500, 30, 30); | |
| 252 fRRectButton.fBounds.setXYWH(this->width() - 50, 550, 30, 30); | |
| 253 this->INHERITED::onSizeChange(); | |
| 254 } | |
| 255 | |
| 256 void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color, | |
| 257 bool show_lines) { | |
| 258 SkPaint paint; | |
| 259 paint.setColor(color); | |
| 260 paint.setAlpha(0x80); | |
| 261 paint.setAntiAlias(true); | |
| 262 int n = path.countPoints(); | |
| 263 SkAutoSTArray<32, SkPoint> pts(n); | |
| 264 if (show_lines) { | |
| 265 path.getPoints(pts.get(), n); | |
| 266 canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts.get(), paint
); | |
| 267 } else { | |
| 268 n = getOnCurvePoints(path, pts.get()); | |
| 269 } | |
| 270 paint.setStrokeWidth(5); | |
| 271 canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint); | |
| 272 } | |
| 273 | |
| 274 void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width, | |
| 275 SkColor color) { | |
| 276 const SkScalar radius = width / 2; | |
| 277 | |
| 278 SkPathMeasure meas(path, false); | |
| 279 SkScalar total = meas.getLength(); | |
| 280 | |
| 281 SkScalar delta = 8; | |
| 282 SkPaint paint; | |
| 283 paint.setColor(color); | |
| 284 | |
| 285 SkPoint pos, tan; | |
| 286 for (SkScalar dist = 0; dist <= total; dist += delta) { | |
| 287 if (meas.getPosTan(dist, &pos, &tan)) { | |
| 288 tan.scale(radius); | |
| 289 tan.rotateCCW(); | |
| 290 canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), | |
| 291 pos.x() - tan.x(), pos.y() - tan.y(), paint); | |
| 292 } | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width) { | |
| 297 SkPaint paint; | |
| 298 paint.setAntiAlias(true); | |
| 299 paint.setStyle(SkPaint::kStroke_Style); | |
| 300 | |
| 301 paint.setColor(SKELETON_COLOR); | |
| 302 canvas->drawPath(path, paint); | |
| 303 draw_points(canvas, path, SKELETON_COLOR, true); | |
| 304 | |
| 305 draw_ribs(canvas, path, width, 0xFF00FF00); | |
| 306 | |
| 307 SkPath fill; | |
| 308 | |
| 309 SkPaint p; | |
| 310 p.setStyle(SkPaint::kStroke_Style); | |
| 311 p.setStrokeWidth(width); | |
| 312 p.getFillPath(path, &fill); | |
| 313 | |
| 314 paint.setColor(WIREFRAME_COLOR); | |
| 315 canvas->drawPath(fill, paint); | |
| 316 draw_points(canvas, fill, WIREFRAME_COLOR, false); | |
| 317 } | |
| 318 | |
| 319 void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) { | |
| 320 SkPaint paint; | |
| 321 paint.setAntiAlias(true); | |
| 322 paint.setStyle(SkPaint::kStroke_Style); | |
| 323 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); | |
| 324 canvas->drawRect(button.fBounds, paint); | |
| 325 paint.setTextSize(25.0f); | |
| 326 paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); | |
| 327 paint.setTextAlign(SkPaint::kCenter_Align); | |
| 328 paint.setStyle(SkPaint::kFill_Style); | |
| 329 canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBo
unds.fBottom - 5, | |
| 330 paint); | |
| 331 } | |
| 332 | |
| 333 void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value, | |
| 334 SkScalar min, SkScalar max, const char* name) { | |
| 335 SkPaint paint; | |
| 336 paint.setAntiAlias(true); | |
| 337 paint.setStyle(SkPaint::kStroke_Style); | |
| 338 canvas->drawRect(bounds, paint); | |
| 339 SkScalar scale = max - min; | |
| 340 SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale; | |
| 341 paint.setColor(0xFFFF0000); | |
| 342 canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint)
; | |
| 343 SkString label; | |
| 344 label.printf("%0.3g", value); | |
| 345 paint.setColor(0xFF000000); | |
| 346 paint.setTextSize(11.0f); | |
| 347 paint.setStyle(SkPaint::kFill_Style); | |
| 348 canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5
, paint); | |
| 349 paint.setTextSize(13.0f); | |
| 350 canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11,
paint); | |
| 351 } | |
| 352 | |
| 353 void onDrawContent(SkCanvas* canvas) SK_OVERRIDE { | |
| 354 SkPath path; | |
| 355 SkScalar width = fWidth; | |
| 356 | |
| 357 if (fCubicButton.fEnabled) { | |
| 358 path.moveTo(fPts[0]); | |
| 359 path.cubicTo(fPts[1], fPts[2], fPts[3]); | |
| 360 draw_stroke(canvas, path, width); | |
| 361 } | |
| 362 | |
| 363 if (fQuadButton.fEnabled) { | |
| 364 path.reset(); | |
| 365 path.moveTo(fPts[4]); | |
| 366 path.quadTo(fPts[5], fPts[6]); | |
| 367 draw_stroke(canvas, path, width); | |
| 368 } | |
| 369 | |
| 370 if (fRRectButton.fEnabled) { | |
| 371 SkScalar rad = 32; | |
| 372 SkRect r; | |
| 373 r.set(&fPts[7], 2); | |
| 374 path.reset(); | |
| 375 SkRRect rr; | |
| 376 rr.setRectXY(r, rad, rad); | |
| 377 path.addRRect(rr); | |
| 378 draw_stroke(canvas, path, width); | |
| 379 | |
| 380 path.reset(); | |
| 381 SkRRect rr2; | |
| 382 rr.inset(width/2, width/2, &rr2); | |
| 383 path.addRRect(rr2, SkPath::kCCW_Direction); | |
| 384 rr.inset(-width/2, -width/2, &rr2); | |
| 385 path.addRRect(rr2, SkPath::kCW_Direction); | |
| 386 SkPaint paint; | |
| 387 paint.setAntiAlias(true); | |
| 388 paint.setColor(0x40FF8844); | |
| 389 canvas->drawPath(path, paint); | |
| 390 } | |
| 391 | |
| 392 if (fAnimate) { | |
| 393 fWidth += fDWidth; | |
| 394 if (fDWidth > 0 && fWidth > kWidthMax) { | |
| 395 fDWidth = -fDWidth; | |
| 396 } else if (fDWidth < 0 && fWidth < kWidthMin) { | |
| 397 fDWidth = -fDWidth; | |
| 398 } | |
| 399 } | |
| 400 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) | |
| 401 draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin
, kStrokerErrorMax, | |
| 402 "error"); | |
| 403 #endif | |
| 404 draw_control(canvas, fWidthControl, fWidth, kWidthMin, kWidthMax, "width
"); | |
| 405 draw_button(canvas, fQuadButton); | |
| 406 draw_button(canvas, fCubicButton); | |
| 407 draw_button(canvas, fRRectButton); | |
| 408 this->inval(NULL); | |
| 409 } | |
| 410 | |
| 411 class MyClick : public Click { | |
| 412 public: | |
| 413 int fIndex; | |
| 414 MyClick(SkView* target, int index) : Click(target), fIndex(index) {} | |
| 415 }; | |
| 416 | |
| 417 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, | |
| 418 unsigned modi) SK_OVERRIDE { | |
| 419 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { | |
| 420 if (hittest(fPts[i], x, y)) { | |
| 421 return new MyClick(this, (int)i); | |
| 422 } | |
| 423 } | |
| 424 const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); | |
| 425 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) | |
| 426 if (fErrorControl.contains(rectPt)) { | |
| 427 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); | |
| 428 } | |
| 429 #endif | |
| 430 if (fWidthControl.contains(rectPt)) { | |
| 431 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); | |
| 432 } | |
| 433 if (fCubicButton.fBounds.contains(rectPt)) { | |
| 434 fCubicButton.fEnabled ^= true; | |
| 435 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); | |
| 436 } | |
| 437 if (fQuadButton.fBounds.contains(rectPt)) { | |
| 438 fQuadButton.fEnabled ^= true; | |
| 439 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); | |
| 440 } | |
| 441 if (fRRectButton.fBounds.contains(rectPt)) { | |
| 442 fRRectButton.fEnabled ^= true; | |
| 443 return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); | |
| 444 } | |
| 445 return this->INHERITED::onFindClickHandler(x, y, modi); | |
| 446 } | |
| 447 | |
| 448 static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min
, | |
| 449 SkScalar max) { | |
| 450 return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min
) + min; | |
| 451 } | |
| 452 | |
| 453 bool onClick(Click* click) SK_OVERRIDE { | |
| 454 int index = ((MyClick*)click)->fIndex; | |
| 455 if (index < (int) SK_ARRAY_COUNT(fPts)) { | |
| 456 fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX
), | |
| 457 SkIntToScalar(click->fICurr.fY - click->fIPrev.fY
)); | |
| 458 this->inval(NULL); | |
| 459 } | |
| 460 #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) | |
| 461 else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) { | |
| 462 gDebugStrokerError = MapScreenYtoValue(click->fICurr.fY, fErrorContr
ol, | |
| 463 kStrokerErrorMin, kStrokerErrorMax); | |
| 464 gDebugStrokerErrorSet = true; | |
| 465 } | |
| 466 #endif | |
| 467 else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) { | |
| 468 fWidth = MapScreenYtoValue(click->fICurr.fY, fWidthControl, kWidthMi
n, kWidthMax); | |
| 469 fAnimate = fWidth <= kWidthMin; | |
| 470 } | |
| 471 return true; | |
| 472 } | |
| 473 | |
| 474 private: | |
| 475 typedef SkView INHERITED; | |
| 476 }; | |
| 477 | |
| 478 /////////////////////////////////////////////////////////////////////////////// | |
| 479 | |
| 480 static SkView* F0() { return new RotateCirclesView; } | |
| 481 static SkViewRegister gR0(F0); | |
| 482 static SkView* F1() { return new TestCirclesView; } | |
| 483 static SkViewRegister gR1(F1); | |
| 484 static SkView* F2() { return new TestStrokeView; } | |
| 485 static SkViewRegister gR2(F2); | |
| OLD | NEW |