diff --git a/.gitignore b/.gitignore index 558dafacc..1c90cb068 100644 --- a/.gitignore +++ b/.gitignore @@ -465,3 +465,6 @@ uwp-project/Assets/gendata uwp-project/Assets/ui_art !uwp-project/devilutionX_TemporaryKey.pfx /.s390x-ccache/ + +# Test fixtures +/test/fixtures/text_render_integration_test/actual.png diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 544cb8415..17d1a430e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,6 +37,9 @@ set(tests timedemo_test writehero_test ) +if(NOT USE_SDL1) + list(APPEND tests text_render_integration_test) +endif() set(standalone_tests codec_test crawl_test diff --git a/test/fixtures/text_render_integration_test/expected.png b/test/fixtures/text_render_integration_test/expected.png new file mode 100644 index 000000000..85648074c Binary files /dev/null and b/test/fixtures/text_render_integration_test/expected.png differ diff --git a/test/text_render_integration_test.cpp b/test/text_render_integration_test.cpp new file mode 100644 index 000000000..1cd25e20f --- /dev/null +++ b/test/text_render_integration_test.cpp @@ -0,0 +1,136 @@ +#include + +#include +#include + +#include +#include +#include + +#include "engine/load_file.hpp" +#include "engine/palette.h" +#include "engine/point.hpp" +#include "engine/rectangle.hpp" +#include "engine/render/primitive_render.hpp" +#include "engine/render/text_render.hpp" +#include "engine/size.hpp" +#include "engine/surface.hpp" +#include "utils/paths.h" +#include "utils/sdl_wrap.h" +#include "utils/surface_to_png.hpp" + +namespace devilution { +namespace { + +constexpr char FixturesPath[] = "../test/fixtures/text_render_integration_test/"; + +SDLPaletteUniquePtr LoadPalette() +{ + struct Color { + uint8_t r, g, b; + }; + std::array palData; + LoadFileInMem("ui_art\\diablo.pal", palData); + SDLPaletteUniquePtr palette = SDLWrap::AllocPalette(256); + for (unsigned i = 0; i < palData.size(); i++) { + palette->colors[i] = SDL_Color { + palData[i].r, palData[i].g, palData[i].b, SDL_ALPHA_OPAQUE + }; + } + return palette; +} + +std::vector ReadFile(const std::string &path) +{ + SDL_RWops *rwops = SDL_RWFromFile(path.c_str(), "rb"); + std::vector result; + if (rwops == nullptr) return result; + const size_t size = SDL_RWsize(rwops); + result.resize(size); + SDL_RWread(rwops, result.data(), size, 1); + SDL_RWclose(rwops); + return result; +} + +void DrawWithBorder(const Surface &out, const Rectangle &area, tl::function_ref fn) +{ + const uint8_t debugColor = PAL8_RED; + DrawHorizontalLine(out, area.position, area.size.width, debugColor); + DrawHorizontalLine(out, area.position + Displacement { 0, area.size.height - 1 }, area.size.width, debugColor); + DrawVerticalLine(out, area.position, area.size.height, debugColor); + DrawVerticalLine(out, area.position + Displacement { area.size.width - 1, 0 }, area.size.height, debugColor); + fn(Rectangle { + Point { area.position.x + 1, area.position.y + 1 }, + Size { area.size.width - 2, area.size.height - 2 } }); +} + +TEST(TextRenderIntegrationTest, GoldenTest) +{ + SDLPaletteUniquePtr palette = LoadPalette(); + OwnedSurface out { Size { 200, 140 } }; + SDL_SetSurfacePalette(out.surface, palette.get()); + ASSERT_NE(out.surface, nullptr); + + int y = -15; + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 96, 15 } }, [&](const Rectangle &rect) { + DrawString(out, "DrawString", rect, + TextRenderOptions { .flags = UiFlags::ColorUiGold }); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 120, 15 } }, [&](const Rectangle &rect) { + DrawString(out, "KerningFitSpacing", rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing | UiFlags::ColorUiSilver }); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 170, 15 } }, [&](const Rectangle &rect) { + DrawString(out, "KerningFitSpacing | AlignCenter", rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing | UiFlags::AlignCenter | UiFlags::ColorUiSilver }); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 170, 15 } }, [&](const Rectangle &rect) { + DrawString(out, "KerningFitSpacing | AlignRight", rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing | UiFlags::AlignRight | UiFlags::ColorUiSilver }); + }); + y += 4; + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 186, 15 } }, [&](const Rectangle &rect) { + DrawStringWithColors(out, "{}{}{}{}", + { { "Draw", UiFlags::ColorUiSilver }, + { "String", UiFlags::ColorUiGold }, + { "With", UiFlags::ColorUiSilverDark }, + { "Colors", UiFlags::ColorUiGoldDark } }, + rect); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 120, 15 } }, [&](const Rectangle &rect) { + DrawStringWithColors(out, "{}{}{}", + { { "Kerning", UiFlags::ColorUiSilver }, + { "Fit", UiFlags::ColorUiGold }, + { "Spacing", UiFlags::ColorUiSilverDark } }, + rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing }); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 170, 15 } }, [&](const Rectangle &rect) { + DrawStringWithColors(out, "{}{}{}", + { { "KerningFitSpacing", UiFlags::ColorUiSilver }, + { " | ", UiFlags::ColorUiGold }, + { "AlignCenter", UiFlags::ColorUiSilverDark } }, + rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing | UiFlags::AlignCenter | UiFlags::ColorUiSilver }); + }); + DrawWithBorder(out, Rectangle { Point { 0, y += 15 }, Size { 170, 15 } }, [&](const Rectangle &rect) { + DrawStringWithColors(out, "{}{}{}", + { { "KerningFitSpacing", UiFlags::ColorUiSilver }, + { " | ", UiFlags::ColorUiGold }, + { "AlignRight", UiFlags::ColorUiSilverDark } }, + rect, + TextRenderOptions { .flags = UiFlags::KerningFitSpacing | UiFlags::AlignRight | UiFlags::ColorUiSilver }); + }); + + const std::string actualPath = paths::BasePath() + FixturesPath + "actual.png"; + const std::string expectedPath = paths::BasePath() + FixturesPath + "expected.png"; + SDL_RWops *actual = SDL_RWFromFile(actualPath.c_str(), "wb"); + ASSERT_NE(actual, nullptr) << SDL_GetError(); + ASSERT_TRUE(WriteSurfaceToFilePng(out, actual).has_value()); + EXPECT_EQ(ReadFile(actualPath), ReadFile(expectedPath)) << "\n" + << expectedPath << "\n" + << actualPath; +} + +} // namespace +} // namespace devilution