| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm.h" |
| #include "sk_tool_utils.h" |
| #include "SkDashPathEffect.h" |
| #include "SkGradientShader.h" |
| #include "SkMaskFilter.h" |
| #include "SkPaint.h" |
| #include "SkPath.h" |
| #include "SkPoint3.h" |
| |
| constexpr int kNumColumns = 6; |
| constexpr int kNumRows = 8; |
| constexpr int kRadius = 40; // radius of the snowflake |
| constexpr int kPad = 5; // padding on both sides of the snowflake |
| constexpr int kNumSpokes = 6; |
| constexpr SkScalar kStrokeWidth = 5.0f; |
| |
| static void draw_fins(SkCanvas* canvas, const SkPoint& offset, float angle, const SkPaint& paint) { |
| SkScalar cos, sin; |
| |
| // first fin |
| sin = SkScalarSinCos(angle + (SK_ScalarPI/4), &cos); |
| sin *= kRadius / 2.0f; |
| cos *= kRadius / 2.0f; |
| |
| SkPath p; |
| p.moveTo(offset.fX, offset.fY); |
| p.lineTo(offset.fX + cos, offset.fY + sin); |
| canvas->drawPath(p, paint); |
| |
| // second fin |
| sin = SkScalarSinCos(angle - (SK_ScalarPI/4), &cos); |
| sin *= kRadius / 2.0f; |
| cos *= kRadius / 2.0f; |
| |
| p.reset(); |
| p.moveTo(offset.fX, offset.fY); |
| p.lineTo(offset.fX + cos, offset.fY + sin); |
| canvas->drawPath(p, paint); |
| } |
| |
| // draw a snowflake centered at the origin |
| static void draw_snowflake(SkCanvas* canvas, const SkPaint& paint) { |
| |
| canvas->clipRect(SkRect::MakeLTRB(-kRadius-kPad, -kRadius-kPad, kRadius+kPad, kRadius+kPad)); |
| |
| SkScalar sin, cos, angle = 0.0f; |
| for (int i = 0; i < kNumSpokes/2; ++i, angle += SK_ScalarPI/(kNumSpokes/2)) { |
| sin = SkScalarSinCos(angle, &cos); |
| sin *= kRadius; |
| cos *= kRadius; |
| |
| // main spoke |
| SkPath p; |
| p.moveTo(-cos, -sin); |
| p.lineTo(cos, sin); |
| canvas->drawPath(p, paint); |
| |
| // fins on positive side |
| const SkPoint posOffset = SkPoint::Make(0.5f * cos, 0.5f * sin); |
| draw_fins(canvas, posOffset, angle, paint); |
| |
| // fins on negative side |
| const SkPoint negOffset = SkPoint::Make(-0.5f * cos, -0.5f * sin); |
| draw_fins(canvas, negOffset, angle+SK_ScalarPI, paint); |
| } |
| } |
| |
| static void draw_row(SkCanvas* canvas, const SkPaint& paint, const SkMatrix& localMatrix) { |
| canvas->translate(kRadius+kPad, 0.0f); |
| |
| for (auto cap : { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap }) { |
| for (auto isAA : { true, false }) { |
| SkPaint tmp(paint); |
| tmp.setStrokeWidth(kStrokeWidth); |
| tmp.setStyle(SkPaint::kStroke_Style); |
| tmp.setStrokeCap(cap); |
| tmp.setAntiAlias(isAA); |
| |
| int saveCount = canvas->save(); |
| canvas->concat(localMatrix); |
| draw_snowflake(canvas, tmp); |
| canvas->restoreToCount(saveCount); |
| |
| canvas->translate(2*(kRadius+kPad), 0.0f); |
| } |
| } |
| } |
| |
| namespace skiagm { |
| |
| // This GM exercises the special case of a stroked lines. |
| // Various shaders are applied to ensure the coordinate spaces work out right. |
| class StrokedLinesGM : public GM { |
| public: |
| StrokedLinesGM() { |
| this->setBGColor(sk_tool_utils::color_to_565(0xFF1A65D7)); |
| } |
| |
| protected: |
| SkString onShortName() override { |
| return SkString("strokedlines"); |
| } |
| |
| SkISize onISize() override { |
| return SkISize::Make(kNumColumns * (2*kRadius+2*kPad), kNumRows * (2*kRadius+2*kPad)); |
| } |
| |
| void onOnceBeforeDraw() override { |
| // paints |
| { |
| // basic white |
| SkPaint p; |
| p.setColor(SK_ColorWHITE); |
| fPaints.push_back(p); |
| } |
| { |
| // gradient |
| SkColor colors[] = { SK_ColorRED, SK_ColorGREEN }; |
| SkPoint pts[] = { {-kRadius-kPad, -kRadius-kPad }, { kRadius+kPad, kRadius+kPad } }; |
| |
| SkPaint p; |
| p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, |
| SkShader::kClamp_TileMode, 0, nullptr)); |
| |
| fPaints.push_back(p); |
| } |
| { |
| // dashing |
| SkScalar intervals[] = { kStrokeWidth, kStrokeWidth }; |
| int intervalCount = (int) SK_ARRAY_COUNT(intervals); |
| SkPaint p; |
| p.setColor(SK_ColorWHITE); |
| p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, kStrokeWidth)); |
| |
| fPaints.push_back(p); |
| } |
| { |
| // Bitmap shader |
| SkBitmap bm; |
| bm.allocN32Pixels(2, 2); |
| *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF; |
| *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = 0x0; |
| |
| SkMatrix m; |
| m.setRotate(12.0f); |
| m.preScale(3.0f, 3.0f); |
| |
| SkPaint p; |
| p.setShader(SkShader::MakeBitmapShader(bm, |
| SkShader::kRepeat_TileMode, |
| SkShader::kRepeat_TileMode, |
| &m)); |
| fPaints.push_back(p); |
| } |
| { |
| // blur |
| SkPaint p; |
| p.setColor(SK_ColorWHITE); |
| p.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3.0f)); |
| fPaints.push_back(p); |
| } |
| |
| // matrices |
| { |
| // rotation |
| SkMatrix m; |
| m.setRotate(12.0f); |
| |
| fMatrices.push_back(m); |
| } |
| { |
| // skew |
| SkMatrix m; |
| m.setSkew(0.3f, 0.5f); |
| |
| fMatrices.push_back(m); |
| } |
| { |
| // perspective |
| SkMatrix m; |
| m.reset(); |
| m.setPerspX(-SK_Scalar1 / 300); |
| m.setPerspY(SK_Scalar1 / 300); |
| |
| fMatrices.push_back(m); |
| } |
| |
| SkASSERT(kNumRows == fPaints.count() + fMatrices.count()); |
| } |
| |
| void onDraw(SkCanvas* canvas) override { |
| canvas->translate(0, kRadius+kPad); |
| |
| for (int i = 0; i < fPaints.count(); ++i) { |
| int saveCount = canvas->save(); |
| draw_row(canvas, fPaints[i], SkMatrix::I()); |
| canvas->restoreToCount(saveCount); |
| |
| canvas->translate(0, 2*(kRadius+kPad)); |
| } |
| |
| for (int i = 0; i < fMatrices.count(); ++i) { |
| int saveCount = canvas->save(); |
| draw_row(canvas, fPaints[0], fMatrices[i]); |
| canvas->restoreToCount(saveCount); |
| |
| canvas->translate(0, 2*(kRadius+kPad)); |
| } |
| } |
| |
| private: |
| SkTArray<SkPaint> fPaints; |
| SkTArray<SkMatrix> fMatrices; |
| |
| typedef GM INHERITED; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| DEF_GM(return new StrokedLinesGM;) |
| } |