From 86cc814f1b36edfe8d2f51164f20b318cd570281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Wed, 23 Feb 2022 23:48:58 +0100 Subject: [PATCH] Utility/GuillotineImageAtlas: Add max layer size --- .../Nazara/Utility/GuillotineImageAtlas.hpp | 3 + src/Nazara/Utility/GuillotineImageAtlas.cpp | 18 ++++- tests/Engine/Utility/FontLoading.cpp | 80 ++++++++++++++----- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/include/Nazara/Utility/GuillotineImageAtlas.hpp b/include/Nazara/Utility/GuillotineImageAtlas.hpp index 92c338276..06dc7a72e 100644 --- a/include/Nazara/Utility/GuillotineImageAtlas.hpp +++ b/include/Nazara/Utility/GuillotineImageAtlas.hpp @@ -29,6 +29,7 @@ namespace Nz void Free(SparsePtr rects, SparsePtr layers, unsigned int count) override; + unsigned int GetMaxLayerSize() const; GuillotineBinPack::FreeRectChoiceHeuristic GetRectChoiceHeuristic() const; GuillotineBinPack::GuillotineSplitHeuristic GetRectSplitHeuristic() const; AbstractImage* GetLayer(unsigned int layerIndex) const override; @@ -37,6 +38,7 @@ namespace Nz bool Insert(const Image& image, Rectui* rect, bool* flipped, unsigned int* layerIndex) override; + void SetMaxLayerSize(unsigned int maxLayerSize); void SetRectChoiceHeuristic(GuillotineBinPack::FreeRectChoiceHeuristic heuristic); void SetRectSplitHeuristic(GuillotineBinPack::GuillotineSplitHeuristic heuristic); @@ -70,6 +72,7 @@ namespace Nz mutable std::vector m_layers; GuillotineBinPack::FreeRectChoiceHeuristic m_rectChoiceHeuristic; GuillotineBinPack::GuillotineSplitHeuristic m_rectSplitHeuristic; + unsigned int m_maxLayerSize; }; } diff --git a/src/Nazara/Utility/GuillotineImageAtlas.cpp b/src/Nazara/Utility/GuillotineImageAtlas.cpp index 8b869a37e..034e622c4 100644 --- a/src/Nazara/Utility/GuillotineImageAtlas.cpp +++ b/src/Nazara/Utility/GuillotineImageAtlas.cpp @@ -15,7 +15,8 @@ namespace Nz GuillotineImageAtlas::GuillotineImageAtlas() : m_rectChoiceHeuristic(GuillotineBinPack::RectBestAreaFit), - m_rectSplitHeuristic(GuillotineBinPack::SplitMinimizeArea) + m_rectSplitHeuristic(GuillotineBinPack::SplitMinimizeArea), + m_maxLayerSize(16384) { } @@ -42,6 +43,11 @@ namespace Nz } } + unsigned int GuillotineImageAtlas::GetMaxLayerSize() const + { + return m_maxLayerSize; + } + GuillotineBinPack::FreeRectChoiceHeuristic GuillotineImageAtlas::GetRectChoiceHeuristic() const { return m_rectChoiceHeuristic; @@ -115,9 +121,10 @@ namespace Nz if (newSize == Vector2ui::Zero()) newSize.Set(s_atlasStartSize); - if (ResizeLayer(layer, newSize)) + // Limit image atlas size to prevent allocating too much contiguous memory blocks + if (newSize.x <= m_maxLayerSize && newSize.y <= m_maxLayerSize && ResizeLayer(layer, newSize)) { - // Oui on peut ! + // Yes we can! layer.binPack.Expand(newSize); // On ajuste l'atlas virtuel // Et on relance la boucle sur la nouvelle dernière couche @@ -149,6 +156,11 @@ namespace Nz return false; } + void GuillotineImageAtlas::SetMaxLayerSize(unsigned int maxLayerSize) + { + m_maxLayerSize = maxLayerSize; + } + void GuillotineImageAtlas::SetRectChoiceHeuristic(GuillotineBinPack::FreeRectChoiceHeuristic heuristic) { m_rectChoiceHeuristic = heuristic; diff --git a/tests/Engine/Utility/FontLoading.cpp b/tests/Engine/Utility/FontLoading.cpp index c55872039..bec12dac5 100644 --- a/tests/Engine/Utility/FontLoading.cpp +++ b/tests/Engine/Utility/FontLoading.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include #include @@ -11,6 +13,11 @@ SCENARIO("Fonts", "[Utility][Font]") { std::shared_ptr font = Nz::Font::GetDefault(); + std::shared_ptr imageAtlas = std::make_shared(); + imageAtlas->SetMaxLayerSize(1024); + + font->SetAtlas(imageAtlas); + CHECK(font->GetFamilyName() == "Open Sans"); CHECK(font->GetStyleName() == "Regular"); @@ -72,28 +79,65 @@ SCENARIO("Fonts", "[Utility][Font]") } } - WHEN("Retrieving a glyph existing in the font") + WHEN("Retrieving glyphs") { - const auto& glyph = font->GetGlyph(72, Nz::TextStyle_Regular, 0.f, 'L'); - REQUIRE(glyph.valid); - CHECK(glyph.advance == 37); - CHECK(glyph.aabb.IsValid()); - CHECK(glyph.atlasRect.IsValid()); - CHECK(glyph.fauxOutlineThickness == 0.f); - CHECK_FALSE(glyph.requireFauxBold); - CHECK_FALSE(glyph.requireFauxItalic); + const auto& glyph1 = font->GetGlyph(72, Nz::TextStyle_Regular, 0.f, 'L'); + REQUIRE(glyph1.valid); + CHECK(glyph1.advance == 37); + CHECK(glyph1.aabb.IsValid()); + CHECK(glyph1.atlasRect.IsValid()); + CHECK(glyph1.fauxOutlineThickness == 0.f); + CHECK_FALSE(glyph1.requireFauxBold); + CHECK_FALSE(glyph1.requireFauxItalic); + + const auto& glyphYItalic = font->GetGlyph(72, Nz::TextStyle::Italic, 0.f, 'y'); + REQUIRE(glyphYItalic.valid); + CHECK(glyphYItalic.advance == 36); + CHECK(glyphYItalic.aabb.IsValid()); + CHECK(glyphYItalic.atlasRect.IsValid()); + CHECK(glyphYItalic.fauxOutlineThickness == 0.f); + CHECK_FALSE(glyphYItalic.requireFauxBold); + CHECK(glyphYItalic.requireFauxItalic); + + const auto& glyphYRegular = font->GetGlyph(72, Nz::TextStyle_Regular, 0.f, 'y'); + REQUIRE(glyphYRegular.valid); + CHECK(glyphYRegular.advance == 36); + CHECK(glyphYRegular.aabb == glyphYItalic.aabb); + CHECK(glyphYRegular.atlasRect == glyphYItalic.atlasRect); + CHECK(glyphYRegular.fauxOutlineThickness == 0.f); + CHECK_FALSE(glyphYRegular.requireFauxBold); + CHECK_FALSE(glyphYRegular.requireFauxItalic); + + CHECK(font->GetCachedGlyphCount() == 3); + CHECK(font->GetAtlas()->GetLayerCount() == 1); + CHECK(font->GetAtlas()->GetLayer(0)->GetLevelCount() == 1); } - WHEN("Retrieving a glyph existing in the font with italic") + WHEN("Precaching a lot of glyphs") { - const auto& glyph = font->GetGlyph(72, Nz::TextStyle::Italic, 0.f, 'y'); - REQUIRE(glyph.valid); - CHECK(glyph.advance == 36); - CHECK(glyph.aabb.IsValid()); - CHECK(glyph.atlasRect.IsValid()); - CHECK(glyph.fauxOutlineThickness == 0.f); - CHECK_FALSE(glyph.requireFauxBold); - CHECK(glyph.requireFauxItalic); + std::string characterSet; + for (char c = 'a'; c <= 'z'; ++c) + characterSet += c; + + for (char c = 'A'; c <= 'Z'; ++c) + characterSet += c; + + for (char c = '0'; c <= '9'; ++c) + characterSet += c; + + for (unsigned int fontSize : {24, 36, 48, 72, 140}) + { + for (float outlineThickness : { 0.f, 1.f, 2.f, 5.f }) + { + font->Precache(fontSize, Nz::TextStyle_Regular, outlineThickness, characterSet); + } + } + + CHECK(font->GetAtlas()->GetLayerCount() > 1); + for (std::size_t layerIndex = 0; layerIndex < font->GetAtlas()->GetLayerCount(); ++layerIndex) + { + CHECK(font->GetAtlas()->GetLayer(layerIndex)->GetLevelCount() == 1); + } } if (i == 1)