| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SampleCode.h" |
| #include "SkView.h" |
| #include "SkCanvas.h" |
| #include "SkRandom.h" |
| #include "SkRRect.h" |
| #include "SkColorPriv.h" |
| #include "SkStrokerPriv.h" |
| |
| static void rotateAbout(SkCanvas* canvas, SkScalar degrees, |
| SkScalar cx, SkScalar cy) { |
| canvas->translate(cx, cy); |
| canvas->rotate(degrees); |
| canvas->translate(-cx, -cy); |
| } |
| |
| class RotateCirclesView : public SampleView { |
| public: |
| RotateCirclesView() { |
| this->setBGColor(SK_ColorLTGRAY); |
| |
| fAngle = 0; |
| } |
| |
| protected: |
| // overrides from SkEventSink |
| virtual bool onQuery(SkEvent* evt) { |
| if (SampleCode::TitleQ(*evt)) { |
| SampleCode::TitleR(evt, "RotateCircles"); |
| return true; |
| } |
| return this->INHERITED::onQuery(evt); |
| } |
| |
| virtual void onDrawContent(SkCanvas* canvas) { |
| SkRandom rand; |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setStrokeWidth(20); |
| |
| SkScalar cx = 240; |
| SkScalar cy = 240; |
| SkScalar DX = 240 * 2; |
| SkColor color = 0; |
| |
| float scale = 1; |
| float sign = 0.3f; |
| for (SkScalar rad = 200; rad >= 20; rad -= 15) { |
| sign = -sign; |
| scale += 0.2f; |
| |
| paint.setColor(rand.nextU()); |
| paint.setAlpha(0xFF); |
| color = ~color; |
| |
| paint.setStyle(SkPaint::kFill_Style); |
| |
| canvas->save(); |
| rotateAbout(canvas, fAngle * scale * sign, cx, cy); |
| canvas->drawCircle(cx, cy, rad, paint); |
| canvas->restore(); |
| |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(rad*2); |
| |
| canvas->save(); |
| rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy); |
| canvas->drawCircle(cx + DX, cy, 10, paint); |
| canvas->restore(); |
| |
| canvas->save(); |
| rotateAbout(canvas, fAngle * scale * sign, cx + DX, cy + DX); |
| canvas->drawCircle(cx + DX, cy + DX, 10, paint); |
| canvas->restore(); |
| |
| } |
| |
| fAngle = (fAngle + 1) % 360; |
| this->inval(NULL); |
| } |
| |
| private: |
| int fAngle; |
| typedef SkView INHERITED; |
| }; |
| |
| class TestCirclesView : public SampleView { |
| public: |
| TestCirclesView() { |
| } |
| |
| protected: |
| virtual bool onQuery(SkEvent* evt) SK_OVERRIDE { |
| if (SampleCode::TitleQ(*evt)) { |
| SampleCode::TitleR(evt, "RotateCircles2"); |
| return true; |
| } |
| return this->INHERITED::onQuery(evt); |
| } |
| |
| void draw_real_circle(SkCanvas* canvas, SkScalar radius) { |
| int w = SkScalarCeilToInt(radius * 2); |
| int h = w; |
| |
| SkBitmap bm; |
| bm.allocN32Pixels(w, h); |
| bm.eraseColor(0); |
| |
| SkAutoLockPixels alp(bm); |
| |
| SkScalar cx = radius; |
| SkScalar cy = radius; |
| for (int y = 0; y < h; y += 1) { |
| for (int x = 0; x < w; x += 1) { |
| float d = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy)); |
| if (d <= radius) { |
| *bm.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0); |
| } |
| } |
| } |
| |
| canvas->drawBitmap(bm, 0, 0, NULL); |
| } |
| |
| virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE { |
| SkScalar radius = 256; |
| canvas->translate(10, 10); |
| |
| draw_real_circle(canvas, radius); |
| |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| |
| paint.setColor(0x80FF0000); |
| canvas->drawCircle(radius, radius, radius, paint); |
| |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(radius); |
| paint.setColor(0x8000FF00); |
| canvas->drawCircle(radius, radius, radius/2, paint); |
| } |
| |
| private: |
| typedef SkView INHERITED; |
| }; |
| |
| static bool hittest(const SkPoint& target, SkScalar x, SkScalar y) { |
| const SkScalar TOL = 7; |
| return SkPoint::Distance(target, SkPoint::Make(x, y)) <= TOL; |
| } |
| |
| static int getOnCurvePoints(const SkPath& path, SkPoint storage[]) { |
| SkPath::RawIter iter(path); |
| SkPoint pts[4]; |
| SkPath::Verb verb; |
| |
| int count = 0; |
| while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { |
| switch (verb) { |
| case SkPath::kMove_Verb: |
| case SkPath::kLine_Verb: |
| case SkPath::kQuad_Verb: |
| case SkPath::kCubic_Verb: |
| storage[count++] = pts[0]; |
| break; |
| default: |
| break; |
| } |
| } |
| return count; |
| } |
| |
| #include "SkPathMeasure.h" |
| |
| struct StrokeTypeButton { |
| SkRect fBounds; |
| char fLabel; |
| bool fEnabled; |
| }; |
| |
| class TestStrokeView : public SampleView { |
| enum { |
| SKELETON_COLOR = 0xFF0000FF, |
| WIREFRAME_COLOR = 0x80FF0000 |
| }; |
| |
| enum { |
| kCount = 9 |
| }; |
| SkPoint fPts[kCount]; |
| SkRect fErrorControl; |
| SkRect fWidthControl; |
| StrokeTypeButton fCubicButton; |
| StrokeTypeButton fQuadButton; |
| StrokeTypeButton fRRectButton; |
| SkScalar fWidth, fDWidth; |
| bool fAnimate; |
| #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) |
| #define kStrokerErrorMin 0.001f |
| #define kStrokerErrorMax 5 |
| #endif |
| #define kWidthMin 1 |
| #define kWidthMax 100 |
| public: |
| TestStrokeView() { |
| this->setBGColor(SK_ColorLTGRAY); |
| |
| fPts[0].set(50, 200); |
| fPts[1].set(50, 100); |
| fPts[2].set(150, 50); |
| fPts[3].set(300, 50); |
| |
| fPts[4].set(350, 200); |
| fPts[5].set(350, 100); |
| fPts[6].set(450, 50); |
| |
| fPts[7].set(200, 200); |
| fPts[8].set(400, 400); |
| |
| fWidth = 50; |
| fDWidth = 0.25f; |
| |
| fCubicButton.fLabel = 'C'; |
| fCubicButton.fEnabled = true; |
| fQuadButton.fLabel = 'Q'; |
| fQuadButton.fEnabled = true; |
| fRRectButton.fLabel = 'R'; |
| fRRectButton.fEnabled = true; |
| fAnimate = true; |
| } |
| |
| protected: |
| virtual bool onQuery(SkEvent* evt) SK_OVERRIDE { |
| if (SampleCode::TitleQ(*evt)) { |
| SampleCode::TitleR(evt, "RotateCircles3"); |
| return true; |
| } |
| return this->INHERITED::onQuery(evt); |
| } |
| |
| virtual void onSizeChange() SK_OVERRIDE { |
| fErrorControl.setXYWH(this->width() - 100, 30, 30, 400); |
| fWidthControl.setXYWH(this->width() - 50, 30, 30, 400); |
| fCubicButton.fBounds.setXYWH(this->width() - 50, 450, 30, 30); |
| fQuadButton.fBounds.setXYWH(this->width() - 50, 500, 30, 30); |
| fRRectButton.fBounds.setXYWH(this->width() - 50, 550, 30, 30); |
| this->INHERITED::onSizeChange(); |
| } |
| |
| void draw_points(SkCanvas* canvas, const SkPath& path, SkColor color, |
| bool show_lines) { |
| SkPaint paint; |
| paint.setColor(color); |
| paint.setAlpha(0x80); |
| paint.setAntiAlias(true); |
| int n = path.countPoints(); |
| SkAutoSTArray<32, SkPoint> pts(n); |
| if (show_lines) { |
| path.getPoints(pts.get(), n); |
| canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts.get(), paint); |
| } else { |
| n = getOnCurvePoints(path, pts.get()); |
| } |
| paint.setStrokeWidth(5); |
| canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts.get(), paint); |
| } |
| |
| void draw_ribs(SkCanvas* canvas, const SkPath& path, SkScalar width, |
| SkColor color) { |
| const SkScalar radius = width / 2; |
| |
| SkPathMeasure meas(path, false); |
| SkScalar total = meas.getLength(); |
| |
| SkScalar delta = 8; |
| SkPaint paint; |
| paint.setColor(color); |
| |
| SkPoint pos, tan; |
| for (SkScalar dist = 0; dist <= total; dist += delta) { |
| if (meas.getPosTan(dist, &pos, &tan)) { |
| tan.scale(radius); |
| tan.rotateCCW(); |
| canvas->drawLine(pos.x() + tan.x(), pos.y() + tan.y(), |
| pos.x() - tan.x(), pos.y() - tan.y(), paint); |
| } |
| } |
| } |
| |
| void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width) { |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setStyle(SkPaint::kStroke_Style); |
| |
| paint.setColor(SKELETON_COLOR); |
| canvas->drawPath(path, paint); |
| draw_points(canvas, path, SKELETON_COLOR, true); |
| |
| draw_ribs(canvas, path, width, 0xFF00FF00); |
| |
| SkPath fill; |
| |
| SkPaint p; |
| p.setStyle(SkPaint::kStroke_Style); |
| p.setStrokeWidth(width); |
| p.getFillPath(path, &fill); |
| |
| paint.setColor(WIREFRAME_COLOR); |
| canvas->drawPath(fill, paint); |
| draw_points(canvas, fill, WIREFRAME_COLOR, false); |
| } |
| |
| void draw_button(SkCanvas* canvas, const StrokeTypeButton& button) { |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); |
| canvas->drawRect(button.fBounds, paint); |
| paint.setTextSize(25.0f); |
| paint.setColor(button.fEnabled ? 0xFF3F0000 : 0x6F3F0000); |
| paint.setTextAlign(SkPaint::kCenter_Align); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas->drawText(&button.fLabel, 1, button.fBounds.centerX(), button.fBounds.fBottom - 5, |
| paint); |
| } |
| |
| void draw_control(SkCanvas* canvas, const SkRect& bounds, SkScalar value, |
| SkScalar min, SkScalar max, const char* name) { |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setStyle(SkPaint::kStroke_Style); |
| canvas->drawRect(bounds, paint); |
| SkScalar scale = max - min; |
| SkScalar yPos = bounds.fTop + (value - min) * bounds.height() / scale; |
| paint.setColor(0xFFFF0000); |
| canvas->drawLine(bounds.fLeft - 5, yPos, bounds.fRight + 5, yPos, paint); |
| SkString label; |
| label.printf("%0.3g", value); |
| paint.setColor(0xFF000000); |
| paint.setTextSize(11.0f); |
| paint.setStyle(SkPaint::kFill_Style); |
| canvas->drawText(label.c_str(), label.size(), bounds.fLeft + 5, yPos - 5, paint); |
| paint.setTextSize(13.0f); |
| canvas->drawText(name, strlen(name), bounds.fLeft, bounds.bottom() + 11, paint); |
| } |
| |
| virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE { |
| SkPath path; |
| SkScalar width = fWidth; |
| |
| if (fCubicButton.fEnabled) { |
| path.moveTo(fPts[0]); |
| path.cubicTo(fPts[1], fPts[2], fPts[3]); |
| draw_stroke(canvas, path, width); |
| } |
| |
| if (fQuadButton.fEnabled) { |
| path.reset(); |
| path.moveTo(fPts[4]); |
| path.quadTo(fPts[5], fPts[6]); |
| draw_stroke(canvas, path, width); |
| } |
| |
| if (fRRectButton.fEnabled) { |
| SkScalar rad = 32; |
| SkRect r; |
| r.set(&fPts[7], 2); |
| path.reset(); |
| SkRRect rr; |
| rr.setRectXY(r, rad, rad); |
| path.addRRect(rr); |
| draw_stroke(canvas, path, width); |
| |
| path.reset(); |
| SkRRect rr2; |
| rr.inset(width/2, width/2, &rr2); |
| path.addRRect(rr2, SkPath::kCCW_Direction); |
| rr.inset(-width/2, -width/2, &rr2); |
| path.addRRect(rr2, SkPath::kCW_Direction); |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setColor(0x40FF8844); |
| canvas->drawPath(path, paint); |
| } |
| |
| if (fAnimate) { |
| fWidth += fDWidth; |
| if (fDWidth > 0 && fWidth > kWidthMax) { |
| fDWidth = -fDWidth; |
| } else if (fDWidth < 0 && fWidth < kWidthMin) { |
| fDWidth = -fDWidth; |
| } |
| } |
| #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) |
| draw_control(canvas, fErrorControl, gDebugStrokerError, kStrokerErrorMin, kStrokerErrorMax, |
| "error"); |
| #endif |
| draw_control(canvas, fWidthControl, fWidth, kWidthMin, kWidthMax, "width"); |
| draw_button(canvas, fQuadButton); |
| draw_button(canvas, fCubicButton); |
| draw_button(canvas, fRRectButton); |
| this->inval(NULL); |
| } |
| |
| class MyClick : public Click { |
| public: |
| int fIndex; |
| MyClick(SkView* target, int index) : Click(target), fIndex(index) {} |
| }; |
| |
| virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, |
| unsigned modi) SK_OVERRIDE { |
| for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); ++i) { |
| if (hittest(fPts[i], x, y)) { |
| return new MyClick(this, (int)i); |
| } |
| } |
| const SkRect& rectPt = SkRect::MakeXYWH(x, y, 1, 1); |
| #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) |
| if (fErrorControl.contains(rectPt)) { |
| return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 1); |
| } |
| #endif |
| if (fWidthControl.contains(rectPt)) { |
| return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 3); |
| } |
| if (fCubicButton.fBounds.contains(rectPt)) { |
| fCubicButton.fEnabled ^= true; |
| return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 4); |
| } |
| if (fQuadButton.fBounds.contains(rectPt)) { |
| fQuadButton.fEnabled ^= true; |
| return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 5); |
| } |
| if (fRRectButton.fBounds.contains(rectPt)) { |
| fRRectButton.fEnabled ^= true; |
| return new MyClick(this, (int) SK_ARRAY_COUNT(fPts) + 6); |
| } |
| return this->INHERITED::onFindClickHandler(x, y, modi); |
| } |
| |
| static SkScalar MapScreenYtoValue(int y, const SkRect& control, SkScalar min, |
| SkScalar max) { |
| return (SkIntToScalar(y) - control.fTop) / control.height() * (max - min) + min; |
| } |
| |
| virtual bool onClick(Click* click) SK_OVERRIDE { |
| int index = ((MyClick*)click)->fIndex; |
| if (index < (int) SK_ARRAY_COUNT(fPts)) { |
| fPts[index].offset(SkIntToScalar(click->fICurr.fX - click->fIPrev.fX), |
| SkIntToScalar(click->fICurr.fY - click->fIPrev.fY)); |
| this->inval(NULL); |
| } |
| #if QUAD_STROKE_APPROXIMATION && defined(SK_DEBUG) |
| else if (index == (int) SK_ARRAY_COUNT(fPts) + 1) { |
| gDebugStrokerError = MapScreenYtoValue(click->fICurr.fY, fErrorControl, |
| kStrokerErrorMin, kStrokerErrorMax); |
| gDebugStrokerErrorSet = true; |
| } |
| #endif |
| else if (index == (int) SK_ARRAY_COUNT(fPts) + 3) { |
| fWidth = MapScreenYtoValue(click->fICurr.fY, fWidthControl, kWidthMin, kWidthMax); |
| fAnimate = fWidth <= kWidthMin; |
| } |
| return true; |
| } |
| |
| private: |
| typedef SkView INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static SkView* F0() { return new RotateCirclesView; } |
| static SkViewRegister gR0(F0); |
| static SkView* F1() { return new TestCirclesView; } |
| static SkViewRegister gR1(F1); |
| static SkView* F2() { return new TestStrokeView; } |
| static SkViewRegister gR2(F2); |