#include #include #include #include #include "ui/animation.hpp" #include "ui/halo.hpp" #include "ui/theme.hpp" using namespace playfin::ui; namespace { bool same(Color a, Color b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } // restores the default accent after a test struct AccentGuard { ~AccentGuard() { applyAccent(accentPalette().front()); } }; } // namespace TEST_CASE("the accent palette: PlayStation blue first, light text only on the dark accents") { const auto& palette = accentPalette(); REQUIRE(palette.size() == 5); CHECK(std::string(palette[0].id) == "playstation"); CHECK(same(palette[0].accent, Color{0x00, 0x70, 0xD1, 0xFF})); CHECK(same(palette[0].onAccent, Color{0xFF, 0xFF, 0xFF, 0xFF})); CHECK(std::string(palette[1].id) == "light"); CHECK(palette[1].onAccent.r < 0x40); // dark text on the light gold CHECK(same(theme().accent, palette[0].accent)); } TEST_CASE("applying an accent changes the theme and its generation") { AccentGuard guard; const unsigned before = themeGeneration(); applyAccent(accentPalette()[2]); CHECK(same(theme().accent, accentPalette()[2].accent)); CHECK(same(theme().onAccent, accentPalette()[2].onAccent)); CHECK(themeGeneration() != before); } TEST_CASE("the identity colours") { const Theme t; CHECK(same(t.background, Color{0x0E, 0x0E, 0x11, 0xFF})); CHECK(same(t.surface, Color{0x1E, 0x1F, 0x25, 0xFF})); CHECK(same(t.surfaceRaised, Color{0x27, 0x28, 0x30, 0xFF})); CHECK(same(t.text, Color{0xF2, 0xF2, 0xF5, 0xFF})); CHECK(same(t.textSecondary, Color{0x9A, 0x9C, 0xA8, 0xFF})); CHECK(t.radius == doctest::Approx(12)); CHECK(t.focusMs == doctest::Approx(250)); } TEST_CASE("the smooth easing starts fast and settles") { CHECK(ease(Easing::Smooth, 0) == doctest::Approx(0)); CHECK(ease(Easing::Smooth, 1) == doctest::Approx(1)); const float half = ease(Easing::Smooth, 0.5f); CHECK(half > 0.7f); CHECK(half < 0.95f); } TEST_CASE("the halo: accent light at the top centre, plain background at the bottom") { const Color background{0x0E, 0x0E, 0x11, 0xFF}; const Color accent{0x00, 0x70, 0xD1, 0xFF}; const std::vector image = haloImage(192, 108, background, accent); REQUIRE(image.size() == 192u * 108u * 4u); for (std::size_t i = 3; i < image.size(); i += 4) { REQUIRE(image[i] == 255); } const std::uint8_t* top = &image[(0 * 192 + 96) * 4]; // at the top centre the light is ~ 30 % of the accent over the background (the centre is above the screen) CHECK(top[2] > background.b + 30); CHECK(top[2] < background.b + 0.3 * (accent.b - background.b) + 8); const std::uint8_t* corner = &image[(107 * 192 + 0) * 4]; CHECK(corner[0] == background.r); CHECK(corner[1] == background.g); CHECK(corner[2] == background.b); } TEST_CASE("the focus glow: strongest under the picture, gone 50 px around it, premultiplied") { const Color accent{0x00, 0x70, 0xD1, 0xFF}; const int w = 100; const int h = 60; const std::vector sprite = glowImage(w, h, 12, 50, accent, 0.45f); const int sw = w + 100; const int sh = h + 100; REQUIRE(sprite.size() == static_cast(sw * sh * 4)); const std::uint8_t* centre = &sprite[(static_cast(sh / 2) * sw + sw / 2) * 4]; CHECK(centre[3] == 115); // 45 % of 255 CHECK(centre[2] <= centre[3]); CHECK(centre[0] == 0); const std::uint8_t* edge = &sprite[(static_cast(sh / 2) * sw + 50) * 4]; // the picture's left edge CHECK(edge[3] > 40); CHECK(edge[3] < 75); const std::uint8_t* corner = &sprite[0]; CHECK(corner[3] == 0); } TEST_CASE("below its lit rows the halo is the plain background") { const Color background{0x0E, 0x0E, 0x11, 0xFF}; const Color accent{0x00, 0x70, 0xD1, 0xFF}; const int width = 192; const int height = 108; const std::vector image = haloImage(width, height, background, accent); const int lit = haloLitRows(height); CHECK(lit < height / 2); for (int y = lit; y < height; ++y) { for (int x = 0; x < width; ++x) { const std::uint8_t* p = &image[(static_cast(y) * width + static_cast(x)) * 4]; REQUIRE(p[0] == background.r); REQUIRE(p[1] == background.g); REQUIRE(p[2] == background.b); } } const std::uint8_t* above = &image[(static_cast(lit / 2) * width + width / 2) * 4]; CHECK(above[2] != background.b); // light inside the lit rows }