diff --git a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp index 97f6dc0da..96aeafd6a 100644 --- a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp +++ b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -185,11 +186,12 @@ Containers::Pointer MagnumFont::doCreateGlyphCache() { Containers::Pointer MagnumFont::doLayout(const AbstractGlyphCache& cache, const Float size, const Containers::StringView text) { /* Get glyph codes from characters */ - Containers::Array glyphs{NoInit, text.size()}; + Containers::Array glyphs; + arrayReserve(glyphs, text.size()); for(std::size_t i = 0; i != text.size(); ) { const Containers::Pair codepointNext = Utility::Unicode::nextChar(text, i); const auto it = _opened->glyphId.find(codepointNext.first()); - glyphs[i] = it == _opened->glyphId.end() ? 0 : it->second; + arrayAppend(glyphs, it == _opened->glyphId.end() ? 0 : it->second); i = codepointNext.second(); } diff --git a/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt b/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt index d4d695bd0..4656e0a51 100644 --- a/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt @@ -36,6 +36,9 @@ endif() if(NOT MAGNUM_MAGNUMFONT_BUILD_STATIC) set(MAGNUMFONT_PLUGIN_FILENAME $) set(TGAIMPORTER_PLUGIN_FILENAME $) + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + set(ANYIMAGEIMPORTER_PLUGIN_FILENAME $) + endif() endif() # First replace ${} variables, then $<> generator expressions @@ -63,16 +66,28 @@ endif() if(MAGNUM_BUILD_GL_TESTS) corrade_add_test(MagnumFontGLTest MagnumFontGLTest.cpp - LIBRARIES MagnumText MagnumTrade MagnumOpenGLTester + LIBRARIES + MagnumDebugTools + MagnumText + MagnumTrade + MagnumOpenGLTester FILES font.conf + font-empty.conf font.tga) target_include_directories(MagnumFontGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_MAGNUMFONT_BUILD_STATIC) - target_link_libraries(MagnumFontGLTest PRIVATE MagnumFont TgaImporter) + target_link_libraries(MagnumFontGLTest PRIVATE + MagnumFont) # TgaImporter should get linked transitively + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + target_link_libraries(MagnumFontGLTest PRIVATE AnyImageImporter) + endif() else() # So the plugins get properly built when building the test add_dependencies(MagnumFontGLTest MagnumFont TgaImporter) + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + add_dependencies(MagnumFontGLTest AnyImageImporter) + endif() endif() if((CORRADE_BUILD_STATIC OR MAGNUM_BUILD_STATIC) AND NOT MAGNUM_MAGNUMFONT_BUILD_STATIC) # CMake < 3.4 does this implicitly, but 3.4+ not anymore (see CMP0065). diff --git a/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp b/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp index 19766fd66..bfdae1b04 100644 --- a/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp +++ b/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp @@ -28,6 +28,13 @@ #include /** @todo remove once AbstractFont is -free */ #include +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" +#ifdef MAGNUM_TARGET_GLES +#include "Magnum/DebugTools/TextureImage.h" +#endif #include "Magnum/GL/OpenGLTester.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/GlyphCache.h" @@ -41,6 +48,7 @@ struct MagnumFontGLTest: GL::OpenGLTester { explicit MagnumFontGLTest(); void createGlyphCache(); + void createGlyphCacheNoGlyphs(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _importerManager{"nonexistent"}; @@ -48,15 +56,20 @@ struct MagnumFontGLTest: GL::OpenGLTester { }; MagnumFontGLTest::MagnumFontGLTest() { - addTests({&MagnumFontGLTest::createGlyphCache}); + addTests({&MagnumFontGLTest::createGlyphCache, + &MagnumFontGLTest::createGlyphCacheNoGlyphs}); - /* Load the plugins directly from the build tree. Otherwise they're static - and already loaded. */ + /* Load the plugins directly from the build tree. Otherwise they're either + static and already loaded or not present in the build tree. */ _fontManager.registerExternalManager(_importerManager); #if defined(TGAIMPORTER_PLUGIN_FILENAME) && defined(MAGNUMFONT_PLUGIN_FILENAME) CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); CORRADE_INTERNAL_ASSERT_OUTPUT(_fontManager.load(MAGNUMFONT_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif + /* Optional plugins that don't have to be here */ + #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif } void MagnumFontGLTest::createGlyphCache() { @@ -64,14 +77,71 @@ void MagnumFontGLTest::createGlyphCache() { CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); - /* Just testing that nothing crashes, asserts or errors */ Containers::Pointer cache = font->createGlyphCache(); - + CORRADE_VERIFY(cache); MAGNUM_VERIFY_NO_GL_ERROR(); + + /* Verify glyph contents */ + CORRADE_COMPARE(cache->glyphCount(), 4); + CORRADE_COMPARE((*cache)[0], std::make_pair( + Vector2i{-16, -8}, + Range2Di{{0, 0}, {32, 16}})); + CORRADE_COMPARE((*cache)[font->glyphId(U'W')], std::make_pair( + Vector2i{9, 26}, + Range2Di{{0, 4}, {40, 64}})); + CORRADE_COMPARE((*cache)[font->glyphId(U'e')], std::make_pair( + Vector2i{9, 4}, + Range2Di{{20, 0}, {128, 48}})); + /* ě has deliberately the same glyph data as e */ + UnsignedInt eId = font->glyphId( + /* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals + but it does it regular strings. Still a problem in MSVC 2022, what a + trash fire, can't you just give up on those codepage insanities + already, ffs?! */ + #if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG) + U'\u011B' + #else + U'ě' + #endif + ); + CORRADE_COMPARE((*cache)[eId], std::make_pair( + Vector2i{9, 4}, + Range2Di{{20, 0}, {128, 48}})); + + if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents"); + + /* Verify the actual texture. It should be the image file verbatim. On GLES + we cannot really verify that the size matches, but at least + something. */ + #ifndef MAGNUM_TARGET_GLES + Image2D image = static_cast(*cache).texture().image(0, {PixelFormat::R8Unorm}); + #else + Image2D image = DebugTools::textureSubImage(static_cast(*cache).texture(), 0, {{}, {8, 4}}, {PixelFormat::R8Unorm}); + #endif + CORRADE_COMPARE_WITH(image, + Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.tga"), + DebugTools::CompareImageToFile{_importerManager}); +} + +void MagnumFontGLTest::createGlyphCacheNoGlyphs() { + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); + + CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font-empty.conf"), 0.0f)); + + Containers::Pointer cache = font->createGlyphCache(); CORRADE_VERIFY(cache); - CORRADE_COMPARE(cache->glyphCount(), 3); + MAGNUM_VERIFY_NO_GL_ERROR(); + + /* There's just the empty glyph added by the cache itself, nothing else */ + CORRADE_COMPARE(cache->glyphCount(), 1); + CORRADE_COMPARE((*cache)[0], std::make_pair( + Vector2i{0, 0}, + Range2Di{{0, 0}, {0, 0}})); - /** @todo properly test contents */ + /* Not testing the image as there's no special codepath taken for it if + there are no glyphs */ } }}}} diff --git a/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp b/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp index 26fa0394c..ed654adb4 100644 --- a/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp +++ b/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp @@ -47,6 +47,7 @@ struct MagnumFontTest: TestSuite::Tester { void nonexistent(); void properties(); void layout(); + void layoutNoGlyphsInCache(); void fileCallbackImage(); void fileCallbackImageNotFound(); @@ -56,10 +57,22 @@ struct MagnumFontTest: TestSuite::Tester { PluginManager::Manager _fontManager{"nonexistent"}; }; +const struct { + const char* name; + const char* string; +} LayoutData[]{ + {"", "Wave"}, + {"UTF-8", "Wavě"}, +}; + MagnumFontTest::MagnumFontTest() { addTests({&MagnumFontTest::nonexistent, - &MagnumFontTest::properties, - &MagnumFontTest::layout, + &MagnumFontTest::properties}); + + addInstancedTests({&MagnumFontTest::layout}, + Containers::arraySize(LayoutData)); + + addTests({&MagnumFontTest::layoutNoGlyphsInCache, &MagnumFontTest::fileCallbackImage, &MagnumFontTest::fileCallbackImageNotFound}); @@ -93,18 +106,39 @@ void MagnumFontTest::properties() { CORRADE_COMPARE(font->ascent(), 25.0f); CORRADE_COMPARE(font->descent(), -10.0f); CORRADE_COMPARE(font->lineHeight(), 39.7333f); - CORRADE_COMPARE(font->glyphCount(), 3); - CORRADE_COMPARE(font->glyphSize(font->glyphId(U'W')), (Vector2{16.0f, 120.0f})); + CORRADE_COMPARE(font->glyphCount(), 4); + CORRADE_COMPARE(font->glyphId(U'W'), 2); + CORRADE_COMPARE(font->glyphId(U'e'), 1); + UnsignedInt eId = font->glyphId( + /* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals + but it does it regular strings. Still a problem in MSVC 2022, what a + trash fire, can't you just give up on those codepage insanities + already, ffs?! */ + #if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG) + U'\u011B' + #else + U'ě' + #endif + ); + CORRADE_COMPARE(eId, 3); + CORRADE_COMPARE(font->glyphSize(font->glyphId(U'W')), (Vector2{8.0f, 44.0f})); CORRADE_COMPARE(font->glyphAdvance(font->glyphId(U'W')), (Vector2{23.0f, 0.0f})); } void MagnumFontTest::layout() { + auto&& data = LayoutData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); - /* Fill the cache with some fake glyphs */ - struct DummyGlyphCache: AbstractGlyphCache { + /* Fill the cache with some fake glyphs. Usually fillGlyphCache() would + happen here, but that requires creating a GL GlyphCache, which would + make it impossible to test w/o a GL context */ + /** @todo clean up this mess, it's also getting increasingly out of sync + with the actual glyph cache data in the font */ + struct: AbstractGlyphCache { using AbstractGlyphCache::AbstractGlyphCache; GlyphCacheFeatures doFeatures() const override { return {}; } @@ -112,8 +146,20 @@ void MagnumFontTest::layout() { } cache{Vector2i{256}}; cache.insert(font->glyphId(U'W'), {25, 34}, {{0, 8}, {16, 128}}); cache.insert(font->glyphId(U'e'), {25, 12}, {{16, 4}, {64, 32}}); - - auto layouter = font->layout(cache, 0.5f, "Wave"); + /* ě has deliberately the same glyph data as e */ + cache.insert(font->glyphId( + /* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals + but it does it regular strings. Still a problem in MSVC 2022, what a + trash fire, can't you just give up on those codepage insanities + already, ffs?! */ + #if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG) + U'\u011B' + #else + U'ě' + #endif + ), {25, 12}, {{16, 4}, {64, 32}}); + + auto layouter = font->layout(cache, 0.5f, data.string); CORRADE_VERIFY(layouter); CORRADE_COMPARE(layouter->glyphCount(), 4); @@ -136,13 +182,53 @@ void MagnumFontTest::layout() { Containers::pair(Range2D{}, Range2D{})); CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); - /* 'e' */ + /* 'e' or 'ě' */ CORRADE_COMPARE(layouter->renderGlyph(3, cursorPosition = {}, rectangle), Containers::pair(Range2D{{0.78125f, 0.375f}, {2.28125f, 1.25f}}, Range2D{{0.0625f, 0.015625f}, {0.25f, 0.125f}})); CORRADE_COMPARE(cursorPosition, Vector2(0.375f, 0.0f)); } +void MagnumFontTest::layoutNoGlyphsInCache() { + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); + + CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); + + /* Tests the case where createGlyphCache() was accidentally not called */ + struct: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } cache{Vector2i{256}}; + + auto layouter = font->layout(cache, 0.5f, "Wave"); + CORRADE_VERIFY(layouter); + CORRADE_COMPARE(layouter->glyphCount(), 4); + + Range2D rectangle; + Vector2 cursorPosition; + + /* Compared to layout(), only the cursor position gets updated, everything + else falls back to the invalid glyph */ + + CORRADE_COMPARE(layouter->renderGlyph(0, cursorPosition = {}, rectangle), + Containers::pair(Range2D{}, Range2D{})); + CORRADE_COMPARE(cursorPosition, Vector2(0.71875f, 0.0f)); + + CORRADE_COMPARE(layouter->renderGlyph(1, cursorPosition = {}, rectangle), + Containers::pair(Range2D{}, Range2D{})); + CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); + + CORRADE_COMPARE(layouter->renderGlyph(2, cursorPosition = {}, rectangle), + Containers::pair(Range2D{}, Range2D{})); + CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); + + CORRADE_COMPARE(layouter->renderGlyph(3, cursorPosition = {}, rectangle), + Containers::pair(Range2D{}, Range2D{})); + CORRADE_COMPARE(cursorPosition, Vector2(0.375f, 0.0f)); +} + void MagnumFontTest::fileCallbackImage() { Containers::Pointer font = _fontManager.instantiate("MagnumFont"); CORRADE_VERIFY(font->features() & FontFeature::FileCallback); diff --git a/src/MagnumPlugins/MagnumFont/Test/configure.h.cmake b/src/MagnumPlugins/MagnumFont/Test/configure.h.cmake index ceb73fe42..e7d4ead91 100644 --- a/src/MagnumPlugins/MagnumFont/Test/configure.h.cmake +++ b/src/MagnumPlugins/MagnumFont/Test/configure.h.cmake @@ -24,5 +24,6 @@ */ #cmakedefine MAGNUMFONT_PLUGIN_FILENAME "${MAGNUMFONT_PLUGIN_FILENAME}" +#cmakedefine ANYIMAGEIMPORTER_PLUGIN_FILENAME "${ANYIMAGEIMPORTER_PLUGIN_FILENAME}" #cmakedefine TGAIMPORTER_PLUGIN_FILENAME "${TGAIMPORTER_PLUGIN_FILENAME}" #define MAGNUMFONT_TEST_DIR "${MAGNUMFONT_TEST_DIR}" diff --git a/src/MagnumPlugins/MagnumFont/Test/font-empty.conf b/src/MagnumPlugins/MagnumFont/Test/font-empty.conf new file mode 100644 index 000000000..7f4fa02c2 --- /dev/null +++ b/src/MagnumPlugins/MagnumFont/Test/font-empty.conf @@ -0,0 +1,10 @@ +# Copy of font.conf with all [char] and [glyph] sections removed. Deliberately +# referencing the same image. +version=1 +image=font.tga +originalImageSize=128 64 +padding=16 8 +fontSize=16 +ascent=25 +descent=-10 +lineHeight=39.7333 diff --git a/src/MagnumPlugins/MagnumFont/Test/font.conf b/src/MagnumPlugins/MagnumFont/Test/font.conf index b305ba032..34296b1fc 100644 --- a/src/MagnumPlugins/MagnumFont/Test/font.conf +++ b/src/MagnumPlugins/MagnumFont/Test/font.conf @@ -1,7 +1,7 @@ version=1 image=font.tga -originalImageSize=1536 1536 -padding=24 24 +originalImageSize=128 64 +padding=16 8 fontSize=16 ascent=25 descent=-10 @@ -18,15 +18,22 @@ glyph=1 [char] unicode=76 glyph=0 +[char] +unicode=11B +glyph=3 [glyph] advance=8 0 -position=24 24 -rectangle=24 24 -24 -24 +position=0 0 +rectangle=16 8 16 8 [glyph] advance=12 0 position=25 12 -rectangle=16 4 64 32 +rectangle=36 8 112 40 [glyph] advance=23 0 position=25 34 -rectangle=0 8 16 128 +rectangle=16 12 24 56 +[glyph] +advance=12 0 +position=25 12 +rectangle=36 8 112 40 diff --git a/src/MagnumPlugins/MagnumFont/Test/font.tga b/src/MagnumPlugins/MagnumFont/Test/font.tga index 711486718..823f2d667 100644 Binary files a/src/MagnumPlugins/MagnumFont/Test/font.tga and b/src/MagnumPlugins/MagnumFont/Test/font.tga differ diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt b/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt index 766d948d8..15e73bd23 100644 --- a/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt @@ -28,9 +28,11 @@ set(CMAKE_FOLDER "MagnumPlugins/MagnumFontConverter/Test") if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(MAGNUMFONTCONVERTER_TEST_DIR ".") set(MAGNUMFONTCONVERTER_TEST_WRITE_DIR "write") set(MAGNUMFONT_TEST_DIR ".") else() + set(MAGNUMFONTCONVERTER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(MAGNUMFONTCONVERTER_TEST_WRITE_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(MAGNUMFONT_TEST_DIR ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/MagnumFont/Test) endif() @@ -38,6 +40,9 @@ endif() if(NOT MAGNUM_MAGNUMFONTCONVERTER_BUILD_STATIC) set(MAGNUMFONTCONVERTER_PLUGIN_FILENAME $) set(TGAIMAGECONVERTER_PLUGIN_FILENAME $) + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + set(ANYIMAGEIMPORTER_PLUGIN_FILENAME $) + endif() if(MAGNUM_WITH_TGAIMPORTER) set(TGAIMPORTER_PLUGIN_FILENAME $) endif() @@ -50,20 +55,31 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(MagnumFontConverterTest MagnumFontConverterTest.cpp - LIBRARIES MagnumText MagnumTrade + LIBRARIES + MagnumDebugTools + MagnumText + MagnumTrade FILES ../../MagnumFont/Test/font.conf - ../../MagnumFont/Test/font.tga) + ../../MagnumFont/Test/font.tga + font-empty-cache.conf + font-empty-cache.tga) target_include_directories(MagnumFontConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_MAGNUMFONTCONVERTER_BUILD_STATIC) target_link_libraries(MagnumFontConverterTest PRIVATE MagnumFontConverter) # TgaImageConverter should get linked transitively + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + target_link_libraries(MagnumFontConverterTest PRIVATE AnyImageImporter) + endif() if(MAGNUM_WITH_TGAIMPORTER) target_link_libraries(MagnumFontConverterTest PRIVATE TgaImporter) endif() else() # So the plugins get properly built when building the test add_dependencies(MagnumFontConverterTest MagnumFontConverter) + if(MAGNUM_WITH_ANYIMAGEIMPORTER) + add_dependencies(MagnumFontConverterTest AnyImageImporter) + endif() if(MAGNUM_WITH_TGAIMPORTER) add_dependencies(MagnumFontConverterTest TgaImporter) endif() diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp b/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp index 9cb521524..4f846d79a 100644 --- a/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp +++ b/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp @@ -25,6 +25,7 @@ #include #include +#include #include /** @todo remove once AbstractFont is -free */ #include #include @@ -32,7 +33,9 @@ #include #include "Magnum/Image.h" +#include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" #include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFontConverter.h" @@ -48,6 +51,7 @@ struct MagnumFontConverterTest: TestSuite::Tester { explicit MagnumFontConverterTest(); void exportFont(); + void exportFontEmptyCache(); void exportFontNoGlyphCacheImageDownload(); /* Explicitly forbid system-wide plugin dependencies */ @@ -58,16 +62,20 @@ struct MagnumFontConverterTest: TestSuite::Tester { MagnumFontConverterTest::MagnumFontConverterTest() { addTests({&MagnumFontConverterTest::exportFont, + &MagnumFontConverterTest::exportFontEmptyCache, &MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload}); - /* Load the plugins directly from the build tree. Otherwise they are static - and already loaded. */ + /* Load the plugins directly from the build tree. Otherwise they're either + static and already loaded or not present in the build tree. */ _fontConverterManager.registerExternalManager(_imageConverterManager); #if defined(TGAIMAGECONVERTER_PLUGIN_FILENAME) && defined(MAGNUMFONTCONVERTER_PLUGIN_FILENAME) CORRADE_INTERNAL_ASSERT_OUTPUT(_imageConverterManager.load(TGAIMAGECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); CORRADE_INTERNAL_ASSERT_OUTPUT(_fontConverterManager.load(MAGNUMFONTCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif /* Optional plugins that don't have to be here */ + #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif #ifdef TGAIMPORTER_PLUGIN_FILENAME CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); #endif @@ -76,6 +84,53 @@ MagnumFontConverterTest::MagnumFontConverterTest() { CORRADE_INTERNAL_ASSERT_OUTPUT(Utility::Path::make(MAGNUMFONTCONVERTER_TEST_WRITE_DIR)); } +class MyFont: public Text::AbstractFont { + private: + void doClose() override { _opened = false; } + bool doIsOpened() const override { return _opened; } + Properties doOpenFile(Containers::StringView, Float) override { + _opened = true; + return {16.0f, 25.0f, -10.0f, 39.7333f, 4}; + } + FontFeatures doFeatures() const override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, Containers::StringView) override { return nullptr; } + + UnsignedInt doGlyphId(const char32_t character) override { + switch(character) { + case U'W': return 2; + case U'e': return 1; + /* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t + literals but it does it regular strings. Still a problem in + MSVC 2022, what a trash fire, can't you just give up on + those codepage insanities already, ffs?! */ + #if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG) + case U'\u011B': + #else + case U'ě': + #endif + return 3; + } + + return 0; + } + + Vector2 doGlyphSize(UnsignedInt) override { return {}; } + + Vector2 doGlyphAdvance(const UnsignedInt glyph) override { + switch(glyph) { + case 0: return {8, 0}; + case 1: + case 3: /* e and ě have the same advance */ + return {12, 0}; + case 2: return {23, 0}; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + bool _opened = false; +}; + void MagnumFontConverterTest::exportFont() { Containers::String confFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.conf"); Containers::String tgaFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.tga"); @@ -85,83 +140,102 @@ void MagnumFontConverterTest::exportFont() { if(Utility::Path::exists(tgaFilename)) CORRADE_VERIFY(Utility::Path::remove(tgaFilename)); - /* Fake font with fake cache */ - class FakeFont: public Text::AbstractFont { - public: - explicit FakeFont(): _opened(false) {} - - private: - void doClose() override { _opened = false; } - bool doIsOpened() const override { return _opened; } - Properties doOpenFile(Containers::StringView, Float) override { - _opened = true; - return {16.0f, 25.0f, -10.0f, 39.7333f, 3}; - } - FontFeatures doFeatures() const override { return {}; } - Containers::Pointer doLayout(const AbstractGlyphCache&, Float, Containers::StringView) override { return nullptr; } + MyFont font; + font.openFile({}, {}); - UnsignedInt doGlyphId(const char32_t character) override { - switch(character) { - case 'W': return 2; - case 'e': return 1; - } + struct: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; - return 0; - } + GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + Image2D doImage() override { + return Image2D{PixelFormat::R8Unorm, {8, 4}, Containers::Array{InPlaceInit, { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v' + }}}; + } + } cache{{128, 64}, {16, 8}}; + /* Override the not found glyph to be in bounds as well */ + cache.insert(0, {}, {{16, 8}, {16, 8}}); + cache.insert(font.glyphId(U'W'), {25, 34}, {{16, 12}, {24, 56}}); + cache.insert(font.glyphId(U'e'), {25, 12}, {{36, 8}, {112, 40}}); + /* ě has deliberately the same glyph data as e */ + cache.insert(font.glyphId( + /* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals + but it does it regular strings. Still a problem in MSVC 2022, what a + trash fire, can't you just give up on those codepage insanities + already, ffs?! */ + #if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG) + U'\u011B' + #else + U'ě' + #endif + ), {25, 12}, {{36, 8}, {112, 40}}); - Vector2 doGlyphSize(UnsignedInt) override { return {}; } + /* Convert the file */ + Containers::Pointer converter = _fontConverterManager.instantiate("MagnumFontConverter"); + CORRADE_VERIFY(converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Waveě")); - Vector2 doGlyphAdvance(const UnsignedInt glyph) override { - switch(glyph) { - case 0: return {8, 0}; - case 1: return {12, 0}; - case 2: return {23, 0}; - } + /* Verify font parameters */ + CORRADE_COMPARE_AS(confFilename, + Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), + TestSuite::Compare::File); - CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ - } + if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents"); - bool _opened; - } font; + /* Verify font image */ + CORRADE_COMPARE_WITH(tgaFilename, + Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.tga"), + DebugTools::CompareImageFile{_importerManager}); +} + +void MagnumFontConverterTest::exportFontEmptyCache() { + Containers::String confFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-empty-cache.conf"); + Containers::String tgaFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-empty-cache.tga"); + /* Remove previously created files */ + if(Utility::Path::exists(confFilename)) + CORRADE_VERIFY(Utility::Path::remove(confFilename)); + if(Utility::Path::exists(tgaFilename)) + CORRADE_VERIFY(Utility::Path::remove(tgaFilename)); + + MyFont font; font.openFile({}, {}); - /* Create fake cache */ - struct MyCache: AbstractGlyphCache { - explicit MyCache(): AbstractGlyphCache{Vector2i{1536}, Vector2i{24}} {} + struct: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; } void doSetImage(const Vector2i&, const ImageView2D&) override {} Image2D doImage() override { - return Image2D{PixelFormat::R8Unorm, Vector2i{256}, Containers::Array{ValueInit, 256*256}}; + return Image2D{PixelFormat::R8Unorm, {8, 4}, Containers::Array{ValueInit, 8*4}}; } - } cache; - cache.insert(font.glyphId(U'W'), {25, 34}, {{0, 8}, {16, 128}}); - cache.insert(font.glyphId(U'e'), {25, 12}, {{16, 4}, {64, 32}}); + } cache{{8, 4}}; /* Convert the file */ Containers::Pointer converter = _fontConverterManager.instantiate("MagnumFontConverter"); - CORRADE_VERIFY(converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave")); + CORRADE_VERIFY(converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-empty-cache"), "Wave")); /* Verify font parameters */ CORRADE_COMPARE_AS(confFilename, - Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), + Utility::Path::join(MAGNUMFONTCONVERTER_TEST_DIR, "font-empty-cache.conf"), TestSuite::Compare::File); - if(!(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("TgaImporter plugin not enabled, not testing glyph cache contents"); - - /* Verify font image, no need to test image contents, as the image is - garbage anyway */ - Containers::Pointer importer = _importerManager.instantiate("TgaImporter"); - CORRADE_VERIFY(importer->openFile(tgaFilename)); - Containers::Optional image = importer->image2D(0); - CORRADE_VERIFY(image); - CORRADE_COMPARE(image->size(), Vector2i(256)); - CORRADE_COMPARE(image->format(), PixelFormat::R8Unorm); + if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents"); + + /* Verify font image */ + CORRADE_COMPARE_WITH(tgaFilename, + Utility::Path::join(MAGNUMFONTCONVERTER_TEST_DIR, "font-empty-cache.tga"), + DebugTools::CompareImageFile{_importerManager}); } void MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload() { - struct MyFont: AbstractFont { + struct: AbstractFont { /* Supports neither file nor data opening */ FontFeatures doFeatures() const override { return {}; } bool doIsOpened() const override { return false; } @@ -175,7 +249,7 @@ void MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload() { } } font; - struct DummyGlyphCache: AbstractGlyphCache { + struct: AbstractGlyphCache { using AbstractGlyphCache::AbstractGlyphCache; GlyphCacheFeatures doFeatures() const override { return {}; } diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/configure.h.cmake b/src/MagnumPlugins/MagnumFontConverter/Test/configure.h.cmake index e2d70544a..db1cf7afa 100644 --- a/src/MagnumPlugins/MagnumFontConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/MagnumFontConverter/Test/configure.h.cmake @@ -25,6 +25,8 @@ #cmakedefine MAGNUMFONTCONVERTER_PLUGIN_FILENAME "${MAGNUMFONTCONVERTER_PLUGIN_FILENAME}" #cmakedefine TGAIMAGECONVERTER_PLUGIN_FILENAME "${TGAIMAGECONVERTER_PLUGIN_FILENAME}" +#cmakedefine ANYIMAGEIMPORTER_PLUGIN_FILENAME "${ANYIMAGEIMPORTER_PLUGIN_FILENAME}" #cmakedefine TGAIMPORTER_PLUGIN_FILENAME "${TGAIMPORTER_PLUGIN_FILENAME}" +#define MAGNUMFONTCONVERTER_TEST_DIR "${MAGNUMFONTCONVERTER_TEST_DIR}" #define MAGNUMFONTCONVERTER_TEST_WRITE_DIR "${MAGNUMFONTCONVERTER_TEST_WRITE_DIR}" #define MAGNUMFONT_TEST_DIR "${MAGNUMFONT_TEST_DIR}" diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.conf b/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.conf new file mode 100644 index 000000000..ef68453df --- /dev/null +++ b/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.conf @@ -0,0 +1,24 @@ +version=1 +image=font-empty-cache.tga +originalImageSize=8 4 +padding=0 0 +fontSize=16 +ascent=25 +descent=-10 +lineHeight=39.7333 +[char] +unicode=57 +glyph=0 +[char] +unicode=61 +glyph=0 +[char] +unicode=65 +glyph=0 +[char] +unicode=76 +glyph=0 +[glyph] +advance=8 0 +position=0 0 +rectangle=0 0 0 0 diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.tga b/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.tga new file mode 100644 index 000000000..e36555264 Binary files /dev/null and b/src/MagnumPlugins/MagnumFontConverter/Test/font-empty-cache.tga differ