| /* |
| * Copyright 2026 Rive |
| */ |
| |
| #include "gm.hpp" |
| #include "gmutils.hpp" |
| #include "rive/math/math_types.hpp" |
| #include "rive/renderer.hpp" |
| #include "common/rand.hpp" |
| |
| using namespace rivegm; |
| using namespace rive; |
| |
| namespace |
| { |
| enum class UseFeather : bool |
| { |
| No, |
| Yes, |
| }; |
| |
| enum class ShapeSize |
| { |
| Small, |
| Large, |
| Stretch, |
| }; |
| |
| template <UseFeather Feather, |
| ShapeSize InnerSize, |
| BlendMode Blend = BlendMode::srcOver> |
| class ClipShapesGM : public GM |
| { |
| public: |
| static constexpr int32_t SectionSize = 400; |
| static constexpr int32_t Spacing = 50; |
| static constexpr int32_t RoundRadius = 50; |
| static constexpr int32_t SectionCount = 3; |
| static constexpr int32_t ImageDim = SectionCount * SectionSize; |
| |
| ClipShapesGM() : GM(ImageDim, ImageDim) {} |
| |
| void onDraw(rive::Renderer* renderer) override |
| { |
| { |
| Paint p; |
| p->color(0xff000000); |
| renderer->drawPath( |
| PathBuilder::Rect( |
| {0.0f, 0.0f, float(ImageDim), float(ImageDim)}), |
| p); |
| } |
| { |
| Paint p; |
| p->color(0xff2c1642); |
| p->feather(10.0f); |
| auto yStep = 40.0f; |
| for (auto y = 20.0f; y < float(SectionCount * SectionSize); y++) |
| { |
| auto path = PathBuilder::Rect({ |
| -100.0f, |
| y, |
| float(ImageDim) + 100.0f, |
| y + yStep * 0.3f, |
| }); |
| path->fillRule(FillRule::clockwise); |
| renderer->drawPath(path, p); |
| y += yStep; |
| yStep *= 1.12f; |
| } |
| } |
| constexpr uint32_t Colors[SectionCount][SectionCount] = { |
| { |
| 0xff881177, |
| 0xffeedd00, |
| 0xff00bbcc, |
| }, |
| { |
| 0xffaa3355, |
| 0xff99dd55, |
| 0xffee9944, |
| }, |
| { |
| 0xffcc6666, |
| 0xff3366bb, |
| 0xff22ccbb, |
| }, |
| }; |
| for (auto yIndex = 0; yIndex < 3; yIndex++) |
| { |
| for (auto xIndex = 0; xIndex < 3; xIndex++) |
| { |
| renderer->save(); |
| { |
| const auto box = AABB{ |
| float(SectionSize * xIndex + Spacing / 2), |
| float(SectionSize * yIndex + Spacing / 2), |
| float(SectionSize * (xIndex + 1) - Spacing / 2), |
| float(SectionSize * (yIndex + 1) - Spacing / 2), |
| }; |
| auto clipper = PathBuilder::RRect(box, |
| float(RoundRadius), |
| float(RoundRadius)); |
| renderer->clipPath(clipper); |
| |
| Paint p; |
| p->color(Colors[xIndex][yIndex]); |
| p->blendMode(Blend); |
| if constexpr (Feather == UseFeather::Yes) |
| { |
| p->feather(float(Spacing)); |
| } |
| |
| if constexpr (InnerSize == ShapeSize::Large) |
| { |
| const auto dim = |
| float(SectionCount * SectionSize) * 0.5f; |
| auto path = PathBuilder::Circle(dim, dim, dim * 0.8f); |
| path->fillRule(FillRule::clockwise); |
| renderer->drawPath(path, p); |
| } |
| else |
| { |
| auto x = lerp(box.right(), |
| box.left(), |
| float(xIndex) / float(SectionCount - 1)); |
| auto y = lerp(box.bottom(), |
| box.top(), |
| float(yIndex) / float(SectionCount - 1)); |
| auto path = |
| PathBuilder::Circle(x, y, SectionSize * 0.24f); |
| path->fillRule(FillRule::clockwise); |
| renderer->drawPath(path, p); |
| } |
| } |
| renderer->restore(); |
| } |
| } |
| } |
| }; |
| } // namespace |
| |
| GMREGISTER(clip_shapes_large_corners, |
| return (new ClipShapesGM<UseFeather::No, ShapeSize::Large>())) |
| GMREGISTER(clip_shapes_large_corners_feathered, |
| return (new ClipShapesGM<UseFeather::Yes, ShapeSize::Large>())) |
| GMREGISTER(clip_shapes_small_corners, |
| return (new ClipShapesGM<UseFeather::No, ShapeSize::Small>())) |
| GMREGISTER(clip_shapes_small_corners_feathered, |
| return (new ClipShapesGM<UseFeather::Yes, ShapeSize::Small>())) |
| GMREGISTER(clip_shapes_large_corners_feathered_blend, |
| return (new ClipShapesGM<UseFeather::Yes, |
| ShapeSize::Large, |
| BlendMode::screen>())) |
| GMREGISTER(clip_shapes_small_corners_feathered_blend, |
| return (new ClipShapesGM<UseFeather::Yes, |
| ShapeSize::Small, |
| BlendMode::screen>())) |