| /* | |
| * Copyright 2016 Google Inc. | |
| * Copyright 2022 Rive | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include <cassert> | |
| #include "gm.hpp" | |
| #include "gmutils.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/math/mat2d.hpp" | |
| #include "rive/math/math_types.hpp" | |
| #include "rive/math/vec2d.hpp" | |
| using namespace rivegm; | |
| using namespace rive; | |
| constexpr int kNumColumns = 3; | |
| constexpr int kNumPaints = 2; | |
| constexpr int kNumRows = 5; | |
| constexpr int kRadius = 40; // radius of the snowflake | |
| constexpr int kPad = 5; // padding on both sides of the snowflake | |
| constexpr int kNumSpokes = 6; | |
| constexpr float kStrokeWidth = 5.0f; | |
| static void draw_fins(Renderer* renderer, | |
| const Vec2D& offset, | |
| float angle, | |
| RenderPaint* paint) | |
| { | |
| float cos, sin; | |
| // first fin | |
| sin = sinf(angle + (math::PI / 4)); | |
| cos = cosf(angle + (math::PI / 4)); | |
| sin *= kRadius / 2.0f; | |
| cos *= kRadius / 2.0f; | |
| Path p; | |
| p->moveTo(offset.x, offset.y); | |
| p->lineTo(offset.x + cos, offset.y + sin); | |
| renderer->drawPath(p, paint); | |
| // second fin | |
| sin = sinf(angle - (math::PI / 4)); | |
| cos = cosf(angle - (math::PI / 4)); | |
| sin *= kRadius / 2.0f; | |
| cos *= kRadius / 2.0f; | |
| p = Path(); | |
| p->moveTo(offset.x, offset.y); | |
| p->lineTo(offset.x + cos, offset.y + sin); | |
| renderer->drawPath(p, paint); | |
| } | |
| // draw a snowflake centered at the origin | |
| static void draw_snowflake(Renderer* renderer, RenderPaint* paint) | |
| { | |
| renderer->clipPath(PathBuilder::Rect( | |
| {-kRadius - kPad, -kRadius - kPad, kRadius + kPad, kRadius + kPad})); | |
| float sin, cos, angle = 0.0f; | |
| for (int i = 0; i < kNumSpokes / 2; | |
| ++i, angle += math::PI / (int)(kNumSpokes / 2)) | |
| { | |
| sin = sinf(angle); | |
| cos = cosf(angle); | |
| sin *= kRadius; | |
| cos *= kRadius; | |
| // main spoke | |
| Path p; | |
| p->moveTo(-cos, -sin); | |
| p->lineTo(cos, sin); | |
| renderer->drawPath(p, paint); | |
| // fins on positive side | |
| const Vec2D posOffset = Vec2D(0.5f * cos, 0.5f * sin); | |
| draw_fins(renderer, posOffset, angle, paint); | |
| // fins on negative side | |
| const Vec2D negOffset = Vec2D(-0.5f * cos, -0.5f * sin); | |
| draw_fins(renderer, negOffset, angle + math::PI, paint); | |
| } | |
| } | |
| static void draw_row(Renderer* renderer, | |
| RenderPaint* paint, | |
| const Mat2D& localMatrix) | |
| { | |
| renderer->translate(kRadius + kPad, 0.0f); | |
| for (auto cap : {StrokeCap::butt, StrokeCap::round, StrokeCap::square}) | |
| { | |
| paint->thickness(kStrokeWidth); | |
| paint->style(RenderPaintStyle::stroke); | |
| paint->cap(cap); | |
| renderer->save(); | |
| renderer->transform(localMatrix); | |
| draw_snowflake(renderer, paint); | |
| renderer->restore(); | |
| renderer->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() : | |
| GM(kNumColumns * (2 * kRadius + 2 * kPad), | |
| kNumRows * (2 * kRadius + 2 * kPad)) | |
| {} | |
| protected: | |
| void onOnceBeforeDraw() override | |
| { | |
| // paints | |
| { | |
| // basic white | |
| fPaints[0]->color(0xffffffff); | |
| } | |
| { | |
| // gradient | |
| ColorInt colors[] = {0xffff0000, 0xff00ff00}; | |
| Vec2D pts[] = {{-kRadius - kPad, -kRadius - kPad}, | |
| {kRadius + kPad, kRadius + kPad}}; | |
| float stops[] = {0, 1}; | |
| auto sh = | |
| TestingWindow::Get()->factory()->makeLinearGradient(pts[0].x, | |
| pts[0].y, | |
| pts[1].x, | |
| pts[1].y, | |
| colors, | |
| stops, | |
| 2); | |
| fPaints[1]->shader(sh); | |
| } | |
| // { | |
| // // dashing | |
| // float 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( | |
| // bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, | |
| // SkSamplingOptions(), 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 | |
| Mat2D m = Mat2D::fromRotation(12.0f * math::PI / 180); | |
| fMatrices.push_back(m); | |
| } | |
| { | |
| // skew | |
| Mat2D m; | |
| m.xy(0.5f); | |
| m.yx(0.3f); | |
| fMatrices.push_back(m); | |
| } | |
| { | |
| // perspective - not supported | |
| Mat2D m; | |
| fMatrices.push_back(m); | |
| } | |
| assert(kNumRows == kNumPaints + fMatrices.size()); | |
| } | |
| ColorInt clearColor() const override { return 0xFF1A65D7; } | |
| void onDraw(rive::Renderer* renderer) override | |
| { | |
| renderer->translate(0, kRadius + kPad); | |
| for (int i = 0; i < kNumPaints; ++i) | |
| { | |
| renderer->save(); | |
| draw_row(renderer, fPaints[i], Mat2D()); | |
| renderer->restore(); | |
| renderer->translate(0, 2 * (kRadius + kPad)); | |
| } | |
| for (size_t i = 0; i < fMatrices.size(); ++i) | |
| { | |
| renderer->save(); | |
| draw_row(renderer, fPaints[0], fMatrices[i]); | |
| renderer->restore(); | |
| renderer->translate(0, 2 * (kRadius + kPad)); | |
| } | |
| } | |
| private: | |
| Paint fPaints[kNumPaints]; | |
| std::vector<Mat2D> fMatrices; | |
| using INHERITED = GM; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| GMREGISTER(strokedlines, return new StrokedLinesGM;) | |
| } // namespace skiagm |