| /* |
| * Copyright 2022 Rive |
| * Copyright 2022 Rive |
| */ |
| |
| #include "gm.hpp" |
| #include "gmutils.hpp" |
| #include "bicubic.hpp" |
| |
| #include "rive/math/math_types.hpp" |
| #include "rive/math/hit_test.hpp" |
| |
| #include "assets/nomoon.png.hpp" |
| |
| class Random |
| { |
| // See "Numerical Recipes in C", 1992 page 284 for these constants |
| // For the LCG that sets the initial state from a seed |
| enum |
| { |
| kMul = 1664525, |
| kAdd = 1013904223 |
| }; |
| uint32_t m_Seed; |
| |
| public: |
| Random(uint32_t seed = 0) : m_Seed(seed) {} |
| |
| uint32_t nextU() |
| { |
| m_Seed = m_Seed * kMul + kAdd; |
| return m_Seed; |
| } |
| |
| float nextF() |
| { |
| double x = this->nextU() * (1.0 / (1 << 30)); |
| return (float)(x - std::floor(x)); |
| } |
| |
| float nextF(float min, float max) |
| { |
| return this->nextF() * (max - min) + min; |
| } |
| }; |
| |
| using namespace rivegm; |
| |
| static void make_patch(rive::Vec2D pts[16], const rive::AABB& r) |
| { |
| const float sx = r.width() / 3; |
| const float sy = r.height() / 3; |
| |
| for (int y = 0; y < 4; ++y) |
| { |
| for (int x = 0; x < 4; ++x) |
| { |
| const int index = y * 4 + x; |
| pts[index] = { |
| r.left() + x * sx, |
| r.top() + y * sy, |
| }; |
| } |
| } |
| } |
| |
| static void perterb_patch(rive::Vec2D pts[16], float scale) |
| { |
| Random rand; |
| auto rf = [&]() { return rand.nextF(-scale, scale); }; |
| |
| for (int i = 0; i < 16; ++i) |
| { |
| auto dx = rf(); |
| auto dy = rf(); |
| pts[i] += {dx, dy}; |
| } |
| } |
| |
| class MeshGM : public GM |
| { |
| public: |
| MeshGM() : GM(800, 600) {} |
| |
| void onDraw(rive::Renderer* ren) override |
| { |
| auto img = LoadImage(assets::nomoon_png()); |
| |
| constexpr float kGridCellSize = 20; |
| Path grid; |
| grid->fillRule(rive::FillRule::evenOdd); |
| for (size_t v = 0; v < 400 / (kGridCellSize * 2); ++v) |
| { |
| grid->addRect(0, v * kGridCellSize * 2, 1600, kGridCellSize); |
| } |
| for (size_t u = 0; u < 1600 / (kGridCellSize * 2); ++u) |
| { |
| grid->addRect(u * kGridCellSize * 2, 0, kGridCellSize, 400); |
| } |
| Paint gray; |
| gray->color(0xa0a0a0a0); |
| |
| BicubicPatch patch; |
| |
| rive::AABB r = {50, 50, 350, 350}; |
| make_patch(patch.m_Pts, r); |
| perterb_patch(patch.m_Pts, 50); |
| auto mesh = patch.mesh(TestingWindow::Get()->factory()); |
| uint32_t vertexCount = |
| mesh.pts ? rive::math::lossless_numeric_cast<uint32_t>( |
| mesh.pts->sizeInBytes() / sizeof(rive::Vec2D)) |
| : 0; |
| uint32_t indexCount = |
| mesh.indices ? rive::math::lossless_numeric_cast<uint32_t>( |
| mesh.indices->sizeInBytes() / sizeof(uint16_t)) |
| : 0; |
| |
| ren->scale(.5f, .5f); |
| |
| constexpr rive::BlendMode blendModes[] = {rive::BlendMode::srcOver, |
| rive::BlendMode::darken, |
| rive::BlendMode::luminosity, |
| rive::BlendMode::exclusion}; |
| |
| // Draw meshes that exercise all variations in the mesh shader: |
| // viewMatrix, opacity, blendMode, clipRect, clip. |
| for (size_t j = 0; j < 3; ++j) |
| { |
| if (j == 1) |
| { |
| ren->clipPath( |
| PathBuilder::Rect({50, 120, 1600 - 50, 800 - 120})); |
| } |
| ren->drawPath(grid, gray); |
| ren->save(); |
| for (size_t i = 0; i < 4; ++i) |
| { |
| ren->save(); |
| ren->translate(200, 200); |
| ren->rotate(i); |
| ren->translate(-200, -200); |
| if (j == 2) |
| { |
| ren->clipPath(PathBuilder::Circle(200, 200, 150)); |
| } |
| if (img != nullptr) |
| { |
| ren->drawImageMesh(img.get(), |
| rive::ImageSampler::LinearClamp(), |
| mesh.pts, |
| mesh.uvs, |
| mesh.indices, |
| vertexCount, |
| indexCount, |
| blendModes[i], |
| 1.f - .1f * (i + 1)); |
| } |
| ren->restore(); |
| ren->translate(400, 0); |
| } |
| ren->restore(); |
| ren->translate(0, 400); |
| } |
| |
| if (false) |
| { |
| Paint paint; |
| for (int i = 0; i < 16; ++i) |
| { |
| auto p = patch.m_Pts[i]; |
| draw_rect(ren, {p.x - 2, p.y - 2, p.x + 3, p.y + 3}, paint); |
| } |
| } |
| } |
| }; |
| GMREGISTER(mesh, return new MeshGM) |
| |
| class MeshHitTestGM : public GM |
| { |
| BicubicPatch::Rec rec; |
| rive::AABB r = {15, 15, 45, 45}; |
| int fN; |
| |
| public: |
| MeshHitTestGM(int N) : GM(60, 60) { fN = N; } |
| |
| void onOnceBeforeDraw() override |
| { |
| BicubicPatch patch; |
| |
| make_patch(patch.m_Pts, r); |
| perterb_patch(patch.m_Pts, 6); |
| |
| rec = patch.buffers(); |
| } |
| |
| void onDraw(rive::Renderer* ren) override |
| { |
| Paint paint; |
| |
| for (float y = 0; y < 60; y += 1) |
| { |
| for (float x = 0; x < 60; x += 1) |
| { |
| auto area = rive::AABB(x, y, x + fN, y + fN); |
| rive::ColorInt color = 0xFF000000; |
| auto ia = area.round(); |
| if (rive::HitTester::testMesh(ia, rec.pts, rec.indices)) |
| { |
| color = 0xFFFFFFFF; |
| } |
| |
| paint->color(color); |
| draw_rect(ren, area, paint.get()); |
| } |
| } |
| } |
| }; |
| |
| GMREGISTER(mesh_ht_7, return new MeshHitTestGM(7)) |
| GMREGISTER(mesh_ht_1, return new MeshHitTestGM(1)) |