diff --git a/CMakeLists.txt b/CMakeLists.txt index ab74c5315..1ffc72df6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ option(WITH_ANYIMAGECONVERTER "Build AnyImageConverter plugin" OFF) option(WITH_ANYSCENEIMPORTER "Build AnySceneImporter plugin" OFF) option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF) option(WITH_MAGNUMFONT "Build MagnumFont plugin" OFF) -cmake_dependent_option(WITH_MAGNUMFONTCONVERTER "Build MagnumFontConverter plugin" OFF "NOT TARGET_GLES" OFF) +option(WITH_MAGNUMFONTCONVERTER "Build MagnumFontConverter plugin" OFF) option(WITH_OBJIMPORTER "Build ObjImporter plugin" OFF) cmake_dependent_option(WITH_TGAIMAGECONVERTER "Build TgaImageConverter plugin" OFF "NOT WITH_MAGNUMFONTCONVERTER" ON) cmake_dependent_option(WITH_TGAIMPORTER "Build TgaImporter plugin" OFF "NOT WITH_MAGNUMFONT" ON) @@ -104,7 +104,7 @@ option(WITH_SHADERS "Build Shaders library" ON) cmake_dependent_option(WITH_TEXT "Build Text library" ON "NOT WITH_FONTCONVERTER;NOT WITH_MAGNUMFONT;NOT WITH_MAGNUMFONTCONVERTER" ON) cmake_dependent_option(WITH_TEXTURETOOLS "Build TextureTools library" ON "NOT WITH_TEXT;NOT WITH_DISTANCEFIELDCONVERTER" ON) cmake_dependent_option(WITH_TRADE "Build Trade library" ON "NOT WITH_MESHTOOLS;NOT WITH_PRIMITIVES;NOT WITH_IMAGECONVERTER;NOT WITH_ANYIMAGEIMPORTER;NOT WITH_ANYIMAGECONVERTER;NOT WITH_ANYSCENEIMPORTER;NOT WITH_OBJIMPORTER;NOT WITH_TGAIMAGECONVERTER;NOT WITH_TGAIMPORTER" ON) -cmake_dependent_option(WITH_GL "Build GL library" ON "NOT WITH_SHADERS;NOT WITH_TEXT;NOT WITH_GL_INFO;NOT WITH_ANDROIDAPPLICATION;NOT WITH_WINDOWLESSIOSAPPLICATION;NOT WITH_CGLCONTEXT;NOT WITH_GLXAPPLICATION;NOT WITH_GLXCONTEXT;NOT WITH_XEGLAPPLICATION;NOT WITH_WINDOWLESSWGLAPPLICATION;NOT WITH_GLXCONTEXT;NOT WITH_XEGLAPPLICATION;NOT WITH_WINDOWLESSWGLAPPLICATION;NOT WITH_WGLCONTEXT;NOT WITH_WINDOWLESSWINDOWSEGLAPPLICATION;NOT WITH_GLUTAPPLICATION;NOT WITH_DISTANCEFIELDCONVERTER;NOT WITH_FONTCONVERTER;NOT WITH_IMAGECONVERTER" ON) +cmake_dependent_option(WITH_GL "Build GL library" ON "NOT WITH_SHADERS;NOT WITH_GL_INFO;NOT WITH_ANDROIDAPPLICATION;NOT WITH_WINDOWLESSIOSAPPLICATION;NOT WITH_CGLCONTEXT;NOT WITH_GLXAPPLICATION;NOT WITH_GLXCONTEXT;NOT WITH_XEGLAPPLICATION;NOT WITH_WINDOWLESSWGLAPPLICATION;NOT WITH_GLXCONTEXT;NOT WITH_XEGLAPPLICATION;NOT WITH_WINDOWLESSWGLAPPLICATION;NOT WITH_WGLCONTEXT;NOT WITH_WINDOWLESSWINDOWSEGLAPPLICATION;NOT WITH_GLUTAPPLICATION;NOT WITH_DISTANCEFIELDCONVERTER;NOT WITH_FONTCONVERTER;NOT WITH_IMAGECONVERTER" ON) option(WITH_PRIMITIVES "Builf Primitives library" ON) option(WITH_VK "Build Vk library" OFF) diff --git a/doc/building.dox b/doc/building.dox index 0e7fb3588..da79fd4d6 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -502,7 +502,7 @@ specify which parts will be built and which not: [OpenAL](https://www.openal.org/), not built by default. - `WITH_DEBUGTOOLS` --- Build the @ref DebugTools library. - `WITH_GL` --- Build the @ref GL library. Enabled automatically if - `WITH_SHADERS` or `WITH_TEXT` is enabled. + `WITH_SHADERS` is enabled. - `WITH_MESHTOOLS` --- Build the @ref MeshTools library. Enables also building of the Trade library. - `WITH_PRIMITIVES` --- Build the @ref Primitives library. Enables also @@ -514,7 +514,7 @@ specify which parts will be built and which not: - `WITH_SHAPES` @m_class{m-label m-danger} **deprecated** --- Build the @ref Shapes library. Enables also building of the SceneGraph library. - `WITH_TEXT` --- Build the @ref Text library. Enables also building of - the TextureTools and GL libraries. + the TextureTools library. - `WITH_TEXTURETOOLS` --- Build the @ref TextureTools library. Enabled automatically if `WITH_TEXT` or `WITH_DISTANCEFIELDCONVERTER` is enabled. - `WITH_TRADE` --- Build the @ref Trade library. diff --git a/doc/changelog.dox b/doc/changelog.dox index 8e269f238..1c54ad05c 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -77,6 +77,15 @@ See also: - Detection of SwiftShader drivers with @ref GL::Context::DetectedDriver::SwiftShader +@subsubsection changelog-latest-new-text Text library + +- A new @ref Text::AbstractGlyphCache base now makes @ref Text::AbstractFont + and @ref Text::AbstractFontConverter independent on the graphics API used, + meaning the @ref Text library now has just an optional dependency on + @ref GL. The @ref Text::Renderer, @ref Text::GlyphCache and + @ref Text::DistanceFieldGlyphCache classes are now built only if + `TARGET_GL` is enabled (done by default). + @subsection changelog-latest-changes Changes and improvements - The @ref ResourceManager class now accepts also diff --git a/doc/custom-buildsystems-order.dot b/doc/custom-buildsystems-order.dot index 79580d61e..f79580048 100644 --- a/doc/custom-buildsystems-order.dot +++ b/doc/custom-buildsystems-order.dot @@ -90,7 +90,7 @@ digraph "Magnum library dependency order" { MagnumShapes -> MagnumSceneGraph [class="m-dim"] MagnumText -> MagnumTextureTools - MagnumText -> MagnumGL + MagnumText -> MagnumGL [style=dotted] MagnumTextureTools -> Magnum MagnumTextureTools -> MagnumGL [style=dotted] diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index a35f64ee9..317ef9bc3 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -398,7 +398,10 @@ set(_MAGNUM_Shaders_DEPENDENCIES GL) if(MAGNUM_BUILD_DEPRECATED) set(_MAGNUM_Shapes_DEPENDENCIES SceneGraph) endif() -set(_MAGNUM_Text_DEPENDENCIES TextureTools GL) +set(_MAGNUM_Text_DEPENDENCIES TextureTools) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_Text_DEPENDENCIES GL) +endif() set(_MAGNUM_TextureTools_DEPENDENCIES ) if(MAGNUM_TARGET_GL) @@ -433,7 +436,7 @@ set(_MAGNUM_EglContext_DEPENDENCIES GL) set(_MAGNUM_GlxContext_DEPENDENCIES GL) set(_MAGNUM_WglContext_DEPENDENCIES GL) -set(_MAGNUM_MagnumFont_DEPENDENCIES Trade TgaImporter) # and below +set(_MAGNUM_MagnumFont_DEPENDENCIES Trade TgaImporter GL) # and below set(_MAGNUM_MagnumFontConverter_DEPENDENCIES Trade TgaImageConverter) # and below set(_MAGNUM_ObjImporter_DEPENDENCIES MeshTools) # and below foreach(_component ${_MAGNUM_PLUGIN_COMPONENT_LIST}) @@ -442,7 +445,7 @@ foreach(_component ${_MAGNUM_PLUGIN_COMPONENT_LIST}) elseif(_component MATCHES ".+(Importer|ImageConverter)") list(APPEND _MAGNUM_${_component}_DEPENDENCIES Trade) elseif(_component MATCHES ".+(Font|FontConverter)") - list(APPEND _MAGNUM_${_component}_DEPENDENCIES Text TextureTools GL) + list(APPEND _MAGNUM_${_component}_DEPENDENCIES Text TextureTools) endif() endforeach() diff --git a/src/Magnum/Text/AbstractFont.cpp b/src/Magnum/Text/AbstractFont.cpp index 9332fbd0c..c1b279463 100644 --- a/src/Magnum/Text/AbstractFont.cpp +++ b/src/Magnum/Text/AbstractFont.cpp @@ -30,7 +30,7 @@ #include #include "Magnum/Math/Functions.h" -#include "Magnum/Text/GlyphCache.h" +#include "Magnum/Text/AbstractGlyphCache.h" #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT #include "Magnum/Text/configure.h" @@ -39,7 +39,7 @@ namespace Magnum { namespace Text { std::string AbstractFont::pluginInterface() { - return "cz.mosra.magnum.Text.AbstractFont/0.2.4"; + return "cz.mosra.magnum.Text.AbstractFont/0.3"; } #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT @@ -125,7 +125,7 @@ auto AbstractFont::doOpenFile(const std::string& filename, const Float size) -> /* Open file */ if(!Utility::Directory::exists(filename)) { - Error() << "Trade::AbstractFont::openFile(): cannot open file" << filename; + Error() << "Text::AbstractFont::openFile(): cannot open file" << filename; return {}; } @@ -153,20 +153,20 @@ Vector2 AbstractFont::glyphAdvance(const UnsignedInt glyph) { return doGlyphAdvance(glyph); } -void AbstractFont::fillGlyphCache(GlyphCache& cache, const std::string& characters) { +void AbstractFont::fillGlyphCache(AbstractGlyphCache& cache, const std::string& characters) { CORRADE_ASSERT(isOpened(), - "Text::AbstractFont::createGlyphCache(): no font opened", ); + "Text::AbstractFont::fillGlyphCache(): no font opened", ); CORRADE_ASSERT(!(features() & Feature::PreparedGlyphCache), "Text::AbstractFont::fillGlyphCache(): feature not supported", ); doFillGlyphCache(cache, Utility::Unicode::utf32(characters)); } -void AbstractFont::doFillGlyphCache(GlyphCache&, const std::u32string&) { +void AbstractFont::doFillGlyphCache(AbstractGlyphCache&, const std::u32string&) { CORRADE_ASSERT(false, "Text::AbstractFont::fillGlyphCache(): feature advertised but not implemented", ); } -Containers::Pointer AbstractFont::createGlyphCache() { +Containers::Pointer AbstractFont::createGlyphCache() { CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", nullptr); CORRADE_ASSERT(features() & Feature::PreparedGlyphCache, @@ -175,12 +175,12 @@ Containers::Pointer AbstractFont::createGlyphCache() { return doCreateGlyphCache(); } -Containers::Pointer AbstractFont::doCreateGlyphCache() { +Containers::Pointer AbstractFont::doCreateGlyphCache() { CORRADE_ASSERT(false, "Text::AbstractFont::createGlyphCache(): feature advertised but not implemented", nullptr); return nullptr; } -Containers::Pointer AbstractFont::layout(const GlyphCache& cache, const Float size, const std::string& text) { +Containers::Pointer AbstractFont::layout(const AbstractGlyphCache& cache, const Float size, const std::string& text) { CORRADE_ASSERT(isOpened(), "Text::AbstractFont::layout(): no font opened", nullptr); return doLayout(cache, size, text); diff --git a/src/Magnum/Text/AbstractFont.h b/src/Magnum/Text/AbstractFont.h index c562a5dff..29657092d 100644 --- a/src/Magnum/Text/AbstractFont.h +++ b/src/Magnum/Text/AbstractFont.h @@ -108,7 +108,7 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * @brief Plugin interface * * @code{.cpp} - * "cz.mosra.magnum.Text.AbstractFont/0.2.4" + * "cz.mosra.magnum.Text.AbstractFont/0.3" * @endcode */ static std::string pluginInterface(); @@ -244,7 +244,7 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * @ref Feature::PreparedGlyphCache do not support partial glyph cache * filling, use @ref createGlyphCache() instead. */ - void fillGlyphCache(GlyphCache& cache, const std::string& characters); + void fillGlyphCache(AbstractGlyphCache& cache, const std::string& characters); /** * @brief Create glyph cache @@ -254,7 +254,7 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * Other fonts support only partial glyph cache filling, see * @ref fillGlyphCache(). */ - Containers::Pointer createGlyphCache(); + Containers::Pointer createGlyphCache(); /** * @brief Layout the text using font's own layouter @@ -266,7 +266,7 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * See @ref Renderer class for more advanced text layouting. * @see @ref fillGlyphCache(), @ref createGlyphCache() */ - Containers::Pointer layout(const GlyphCache& cache, Float size, const std::string& text); + Containers::Pointer layout(const AbstractGlyphCache& cache, Float size, const std::string& text); protected: /** @@ -349,13 +349,13 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * The string is converted from UTF-8 to UTF-32, unique characters are * *not* removed. */ - virtual void doFillGlyphCache(GlyphCache& cache, const std::u32string& characters); + virtual void doFillGlyphCache(AbstractGlyphCache& cache, const std::u32string& characters); /** @brief Implementation for @ref createGlyphCache() */ - virtual Containers::Pointer doCreateGlyphCache(); + virtual Containers::Pointer doCreateGlyphCache(); /** @brief Implementation for @ref layout() */ - virtual Containers::Pointer doLayout(const GlyphCache& cache, Float size, const std::string& text) = 0; + virtual Containers::Pointer doLayout(const AbstractGlyphCache& cache, Float size, const std::string& text) = 0; #ifdef DOXYGEN_GENERATING_OUTPUT private: diff --git a/src/Magnum/Text/AbstractFontConverter.cpp b/src/Magnum/Text/AbstractFontConverter.cpp index 62c535bd5..7b5d7abf7 100644 --- a/src/Magnum/Text/AbstractFontConverter.cpp +++ b/src/Magnum/Text/AbstractFontConverter.cpp @@ -31,7 +31,7 @@ #include #include -#include "Magnum/Text/GlyphCache.h" +#include "Magnum/Text/AbstractGlyphCache.h" #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT #include "Magnum/Text/configure.h" @@ -56,7 +56,7 @@ std::u32string uniqueUnicode(const std::string& characters) } std::string AbstractFontConverter::pluginInterface() { - return "cz.mosra.magnum.Text.AbstractFontConverter/0.1.2"; + return "cz.mosra.magnum.Text.AbstractFontConverter/0.2"; } #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT @@ -77,14 +77,14 @@ AbstractFontConverter::AbstractFontConverter() = default; AbstractFontConverter::AbstractFontConverter(PluginManager::AbstractManager& manager, const std::string& plugin): PluginManager::AbstractPlugin{manager, plugin} {} -std::vector>> AbstractFontConverter::exportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const { +std::vector>> AbstractFontConverter::exportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::string& characters) const { CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), "Text::AbstractFontConverter::exportFontToData(): feature not supported", {}); return doExportFontToData(font, cache, filename, uniqueUnicode(characters)); } -std::vector>> AbstractFontConverter::doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const { +std::vector>> AbstractFontConverter::doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const { CORRADE_ASSERT(!(features() & Feature::MultiFile), "Text::AbstractFontConverter::exportFontToData(): feature advertised but not implemented", {}); @@ -93,7 +93,7 @@ std::vector>> AbstractFontConvert return out; } -Containers::Array AbstractFontConverter::exportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::string& characters) const { +Containers::Array AbstractFontConverter::exportFontToSingleData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& characters) const { CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), "Text::AbstractFontConverter::exportFontToSingleData(): feature not supported", nullptr); CORRADE_ASSERT(!(features() & Feature::MultiFile), @@ -102,20 +102,20 @@ Containers::Array AbstractFontConverter::exportFontToSingleData(AbstractFo return doExportFontToSingleData(font, cache, uniqueUnicode(characters)); } -Containers::Array AbstractFontConverter::doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string&) const { +Containers::Array AbstractFontConverter::doExportFontToSingleData(AbstractFont&, AbstractGlyphCache&, const std::u32string&) const { CORRADE_ASSERT(false, "Text::AbstractFontConverter::exportFontToSingleData(): feature advertised but not implemented", nullptr); return nullptr; } -bool AbstractFontConverter::exportFontToFile(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const { +bool AbstractFontConverter::exportFontToFile(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::string& characters) const { CORRADE_ASSERT(features() & Feature::ExportFont, "Text::AbstractFontConverter::exportFontToFile(): feature not supported", false); return doExportFontToFile(font, cache, filename, uniqueUnicode(characters)); } -bool AbstractFontConverter::doExportFontToFile(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const { +bool AbstractFontConverter::doExportFontToFile(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const { CORRADE_ASSERT(features() & Feature::ConvertData, "Text::AbstractFontConverter::exportFontToFile(): not implemented", false); @@ -129,14 +129,14 @@ bool AbstractFontConverter::doExportFontToFile(AbstractFont& font, GlyphCache& c return true; } -std::vector>> AbstractFontConverter::exportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const { +std::vector>> AbstractFontConverter::exportGlyphCacheToData(AbstractGlyphCache& cache, const std::string& filename) const { CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), "Text::AbstractFontConverter::exportGlyphCacheToData(): feature not supported", {}); return doExportGlyphCacheToData(cache, filename); } -std::vector>> AbstractFontConverter::doExportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const { +std::vector>> AbstractFontConverter::doExportGlyphCacheToData(AbstractGlyphCache& cache, const std::string& filename) const { CORRADE_ASSERT(!(features() & Feature::MultiFile), "Text::AbstractFontConverter::exportGlyphCacheToData(): feature advertised but not implemented", {}); @@ -145,7 +145,7 @@ std::vector>> AbstractFontConvert return out; } -Containers::Array AbstractFontConverter::exportGlyphCacheToSingleData(GlyphCache& cache) const { +Containers::Array AbstractFontConverter::exportGlyphCacheToSingleData(AbstractGlyphCache& cache) const { CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature not supported", nullptr); CORRADE_ASSERT(!(features() & Feature::MultiFile), @@ -154,20 +154,20 @@ Containers::Array AbstractFontConverter::exportGlyphCacheToSingleData(Glyp return doExportGlyphCacheToSingleData(cache); } -Containers::Array AbstractFontConverter::doExportGlyphCacheToSingleData(GlyphCache&) const { +Containers::Array AbstractFontConverter::doExportGlyphCacheToSingleData(AbstractGlyphCache&) const { CORRADE_ASSERT(false, "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature advertised but not implemented", nullptr); return nullptr; } -bool AbstractFontConverter::exportGlyphCacheToFile(GlyphCache& cache, const std::string& filename) const { +bool AbstractFontConverter::exportGlyphCacheToFile(AbstractGlyphCache& cache, const std::string& filename) const { CORRADE_ASSERT(features() & Feature::ExportGlyphCache, "Text::AbstractFontConverter::exportGlyphCacheToFile(): feature not supported", false); return doExportGlyphCacheToFile(cache, filename); } -bool AbstractFontConverter::doExportGlyphCacheToFile(GlyphCache& cache, const std::string& filename) const { +bool AbstractFontConverter::doExportGlyphCacheToFile(AbstractGlyphCache& cache, const std::string& filename) const { CORRADE_ASSERT(features() & Feature::ConvertData, "Text::AbstractFontConverter::exportGlyphCacheToFile(): not implemented", false); @@ -181,7 +181,7 @@ bool AbstractFontConverter::doExportGlyphCacheToFile(GlyphCache& cache, const st return true; } -Containers::Pointer AbstractFontConverter::importGlyphCacheFromData(const std::vector>>& data) const { +Containers::Pointer AbstractFontConverter::importGlyphCacheFromData(const std::vector>>& data) const { CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), "Text::AbstractFontConverter::importGlyphCacheFromData(): feature not supported", nullptr); CORRADE_ASSERT(!data.empty(), @@ -190,7 +190,7 @@ Containers::Pointer AbstractFontConverter::importGlyphCacheFromData( return doImportGlyphCacheFromData(data); } -Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromData(const std::vector>>& data) const { +Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromData(const std::vector>>& data) const { CORRADE_ASSERT(!(features() & Feature::MultiFile), "Text::AbstractFontConverter::importGlyphCacheFromData(): feature advertised but not implemented", nullptr); CORRADE_ASSERT(data.size() == 1, @@ -199,7 +199,7 @@ Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromDat return doImportGlyphCacheFromSingleData(data[0].second); } -Containers::Pointer AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayView data) const { +Containers::Pointer AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayView data) const { CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature not supported", nullptr); CORRADE_ASSERT(!(features() & Feature::MultiFile), @@ -208,20 +208,20 @@ Containers::Pointer AbstractFontConverter::importGlyphCacheFromSingl return doImportGlyphCacheFromSingleData(data); } -Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayView) const { +Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayView) const { CORRADE_ASSERT(false, "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature advertised but not implemented", nullptr); return nullptr; } -Containers::Pointer AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const { +Containers::Pointer AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const { CORRADE_ASSERT(features() & Feature::ImportGlyphCache, "Text::AbstractFontConverter::importGlyphCacheFromFile(): feature not supported", nullptr); return doImportGlyphCacheFromFile(filename); } -Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const { +Containers::Pointer AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const { CORRADE_ASSERT(features() & Feature::ConvertData && !(features() & Feature::MultiFile), "Text::AbstractFontConverter::importGlyphCacheFromFile(): not implemented", nullptr); diff --git a/src/Magnum/Text/AbstractFontConverter.h b/src/Magnum/Text/AbstractFontConverter.h index bd7e00580..6db291696 100644 --- a/src/Magnum/Text/AbstractFontConverter.h +++ b/src/Magnum/Text/AbstractFontConverter.h @@ -134,7 +134,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @brief Plugin interface * * @code{.cpp} - * "cz.mosra.magnum.Text.AbstractFontConverter/0.1.2" + * "cz.mosra.magnum.Text.AbstractFontConverter/0.2" * @endcode */ static std::string pluginInterface(); @@ -182,7 +182,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportFontToFile(), * @ref exportGlyphCacheToData() */ - std::vector>> exportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const; + std::vector>> exportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::string& characters) const; /** * @brief Export font to single raw data @@ -194,7 +194,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportFontToFile(), * @ref exportGlyphCacheToSingleData() */ - Containers::Array exportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::string& characters) const; + Containers::Array exportFontToSingleData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& characters) const; /** * @brief Export font to file @@ -207,7 +207,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportFontToData(), * @ref exportGlyphCacheToFile() */ - bool exportFontToFile(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const; + bool exportFontToFile(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::string& characters) const; /** * @brief Export glyph cache to raw data @@ -226,7 +226,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportGlyphCacheToFile(), * @ref exportFontToData() */ - std::vector>> exportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const; + std::vector>> exportGlyphCacheToData(AbstractGlyphCache& cache, const std::string& filename) const; /** * @brief Export glyph cache to single raw data @@ -238,7 +238,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportGlyphCacheToFile(), * @ref importGlyphCacheFromSingleData() */ - Containers::Array exportGlyphCacheToSingleData(GlyphCache& cache) const; + Containers::Array exportGlyphCacheToSingleData(AbstractGlyphCache& cache) const; /** * @brief Export glyph cache to file @@ -251,7 +251,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref exportGlyphCacheToData(), * @ref exportFontToFile() */ - bool exportGlyphCacheToFile(GlyphCache& cache, const std::string& filename) const; + bool exportGlyphCacheToFile(AbstractGlyphCache& cache, const std::string& filename) const; /** * @brief Import glyph cache from raw data @@ -265,7 +265,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref importGlyphCacheFromFile(), * @ref exportGlyphCacheToData() */ - Containers::Pointer importGlyphCacheFromData(const std::vector>>& data) const; + Containers::Pointer importGlyphCacheFromData(const std::vector>>& data) const; /** * @brief Import glyph cache from single raw data @@ -277,7 +277,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref importGlyphCacheFromFile(), * @ref exportFontToSingleData() */ - Containers::Pointer importGlyphCacheFromSingleData(Containers::ArrayView data) const; + Containers::Pointer importGlyphCacheFromSingleData(Containers::ArrayView data) const; /** * @brief Import glyph cache from file @@ -290,7 +290,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * @see @ref features(), @ref importGlyphCacheFromData(), * @ref exportGlyphCacheToFile() */ - Containers::Pointer importGlyphCacheFromFile(const std::string& filename) const; + Containers::Pointer importGlyphCacheFromFile(const std::string& filename) const; private: /** @brief Implementation for @ref features() */ @@ -302,10 +302,10 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * If the plugin doesn't have @ref Feature::MultiFile, default * implementation calls @ref doExportFontToSingleData(). */ - virtual std::vector>> doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const; + virtual std::vector>> doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const; /** @brief Implementation for @ref exportFontToSingleData() */ - virtual Containers::Array doExportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::u32string& characters) const; + virtual Containers::Array doExportFontToSingleData(AbstractFont& font, AbstractGlyphCache& cache, const std::u32string& characters) const; /** * @brief Implementation for @ref exportFontToFile() @@ -314,7 +314,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * calls @ref doExportFontToData() and saves the result to given * file(s). */ - virtual bool doExportFontToFile(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const; + virtual bool doExportFontToFile(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const; /** * @brief Implementation for @ref exportGlyphCacheToData() @@ -322,10 +322,10 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * If the plugin doesn't have @ref Feature::MultiFile, default * implementation calls @ref doExportGlyphCacheToSingleData(). */ - virtual std::vector>> doExportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const; + virtual std::vector>> doExportGlyphCacheToData(AbstractGlyphCache& cache, const std::string& filename) const; /** @brief Implementation for @ref exportGlyphCacheToSingleData() */ - virtual Containers::Array doExportGlyphCacheToSingleData(GlyphCache& cache) const; + virtual Containers::Array doExportGlyphCacheToSingleData(AbstractGlyphCache& cache) const; /** * @brief Implementation for @ref exportGlyphCacheToFile() @@ -334,7 +334,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * calls @ref doExportGlyphCacheToData() and saves the result to given * file(s). */ - virtual bool doExportGlyphCacheToFile(GlyphCache& cache, const std::string& filename) const; + virtual bool doExportGlyphCacheToFile(AbstractGlyphCache& cache, const std::string& filename) const; /** * @brief Implementation for @ref importGlyphCacheFromData() @@ -342,10 +342,10 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * If the plugin doesn't have @ref Feature::MultiFile, default * implementation calls @ref doImportGlyphCacheFromSingleData(). */ - virtual Containers::Pointer doImportGlyphCacheFromData(const std::vector>>& data) const; + virtual Containers::Pointer doImportGlyphCacheFromData(const std::vector>>& data) const; /** @brief Implementation for @ref importGlyphCacheFromSingleData() */ - virtual Containers::Pointer doImportGlyphCacheFromSingleData(Containers::ArrayView data) const; + virtual Containers::Pointer doImportGlyphCacheFromSingleData(Containers::ArrayView data) const; /** * @brief Implementation for @ref importGlyphCacheFromFile() @@ -354,7 +354,7 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * have @ref Feature::MultiFile, default implementation opens the file * and calls @ref doImportGlyphCacheFromSingleData() with its contents. */ - virtual Containers::Pointer doImportGlyphCacheFromFile(const std::string& filename) const; + virtual Containers::Pointer doImportGlyphCacheFromFile(const std::string& filename) const; }; CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features) diff --git a/src/Magnum/Text/AbstractGlyphCache.cpp b/src/Magnum/Text/AbstractGlyphCache.cpp new file mode 100644 index 000000000..36e919b01 --- /dev/null +++ b/src/Magnum/Text/AbstractGlyphCache.cpp @@ -0,0 +1,76 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "AbstractGlyphCache.h" + +#include "Magnum/Image.h" +#include "Magnum/TextureTools/Atlas.h" + +namespace Magnum { namespace Text { + +AbstractGlyphCache::AbstractGlyphCache(const Vector2i& size, const Vector2i& padding): _size{size}, _padding{padding} { + /* Default "Not Found" glyph. Can't do just `.insert({0, {}})` because + that's ambiguous in C++17, due to a new insert(node_type&&) overload. */ + glyphs.insert({0, std::pair{}}); +} + +AbstractGlyphCache::~AbstractGlyphCache() = default; + +std::vector AbstractGlyphCache::reserve(const std::vector& sizes) { + CORRADE_ASSERT((glyphs.size() == 1 && glyphs.at(0) == std::pair()), + "Text::AbstractGlyphCache::reserve(): reserving space in non-empty cache is not yet implemented", {}); + glyphs.reserve(glyphs.size() + sizes.size()); + return TextureTools::atlas(_size, sizes, _padding); +} + +void AbstractGlyphCache::insert(const UnsignedInt glyph, const Vector2i& position, const Range2Di& rectangle) { + const std::pair glyphData = {position-_padding, rectangle.padded(_padding)}; + + /* Overwriting "Not Found" glyph */ + if(glyph == 0) glyphs[0] = glyphData; + + /* Inserting new glyph */ + else CORRADE_INTERNAL_ASSERT_OUTPUT(glyphs.insert({glyph, glyphData}).second); +} + +void AbstractGlyphCache::setImage(const Vector2i& offset, const ImageView2D& image) { + CORRADE_ASSERT((offset >= Vector2i{} && offset + image.size() <= _size).all(), + "Text::AbstractGlyphCache::setImage():" << Range2Di::fromSize(offset, image.size()) << "out of bounds for texture size" << _size, ); + + doSetImage(offset, image); +} + +Image2D AbstractGlyphCache::image() { + CORRADE_ASSERT(features() & GlyphCacheFeature::ImageDownload, + "Text::AbstractGlyphCache::image(): feature not supported", Image2D{{}}); + + return doImage(); +} + +Image2D AbstractGlyphCache::doImage() { + CORRADE_ASSERT(false, "Text::AbstractGlyphCache::image(): feature advertised but not implemented", Image2D{{}}); +} + +}} diff --git a/src/Magnum/Text/AbstractGlyphCache.h b/src/Magnum/Text/AbstractGlyphCache.h new file mode 100644 index 000000000..61d2350b8 --- /dev/null +++ b/src/Magnum/Text/AbstractGlyphCache.h @@ -0,0 +1,203 @@ +#ifndef Magnum_Text_AbstractGlyphCache_h +#define Magnum_Text_AbstractGlyphCache_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Text::AbstractGlyphCache + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Range.h" +#include "Magnum/Text/visibility.h" + +namespace Magnum { namespace Text { + +/** +@brief Features supported by a particular glyph cache implementation + +@see @ref GlyphCacheFeatures, @ref AbstractGlyphCache::features() +*/ +enum class GlyphCacheFeature: UnsignedByte { + /** + * Ability to download glyph cache data using + * @ref AbstractGlyphCache::image(). May not be supported by glyph caches + * on embedded platforms that don't have an ability to get texture data + * back from a GPU. + */ + ImageDownload = 1 << 0 +}; + +/** +@brief Set of features supported by a glyph cache + +@see @ref AbstractGlyphCache::features() +*/ +typedef Containers::EnumSet GlyphCacheFeatures; + +CORRADE_ENUMSET_OPERATORS(GlyphCacheFeatures) + +/** +@brief Base for glyph caches + +An API-agnostic base for glyph caches. See @ref GlyphCache and +@ref DistanceFieldGlyphCache for concrete implementations. + +@section Text-AbstractGlyphCache-subclassing Subclassing + +The subclass needs to implement the @ref doSetImage() function and manage the +glyph cache image. The public @ref setImage() function already does checking +for rectangle bounds so it's not needed to do it again on the implementation +side. +*/ +class MAGNUM_TEXT_EXPORT AbstractGlyphCache { + public: + /** + * @brief Constructor + * @param size Glyph cache texture size + * @param padding Padding around every glyph + */ + explicit AbstractGlyphCache(const Vector2i& size, const Vector2i& padding = {}); + + virtual ~AbstractGlyphCache(); + + /** @brief Features supported by this glyph cache implementation */ + GlyphCacheFeatures features() const { return doFeatures(); } + + /** Glyph cache texture size */ + Vector2i textureSize() const { return _size; } + + /** @brief Glyph padding */ + Vector2i padding() const { return _padding; } + + /** @brief Count of glyphs in the cache */ + std::size_t glyphCount() const { return glyphs.size(); } + + /** + * @brief Parameters of given glyph + * @param glyph Glyph ID + * + * First tuple element is glyph position relative to point on baseline, + * second element is glyph region in texture atlas. + * + * Returned values include padding. + * + * If no glyph is found, glyph @cpp 0 @ce is returned, which is by + * default on zero position and has zero region in texture atlas. You + * can reset it to some meaningful value in @ref insert(). + * @see @ref padding() + */ + std::pair operator[](UnsignedInt glyph) const { + auto it = glyphs.find(glyph); + return it == glyphs.end() ? glyphs.at(0) : it->second; + } + + /** @brief Iterator access to cache data */ + std::unordered_map>::const_iterator begin() const { + return glyphs.begin(); + } + + /** @brief Iterator access to cache data */ + std::unordered_map>::const_iterator end() const { + return glyphs.end(); + } + + /** + * @brief Layout glyphs with given sizes to the cache + * + * Returns non-overlapping regions in cache texture to store glyphs. + * The reserved space is reused on next call to @ref reserve() if no + * glyph was stored there, use @ref insert() to store actual glyph on + * given position and @ref setImage() to upload glyph image. + * + * Glyph @p sizes are expected to be without padding. + * + * @attention Cache size must be large enough to contain all rendered + * glyphs. + * @see @ref padding() + */ + std::vector reserve(const std::vector& sizes); + + /** + * @brief Insert glyph to cache + * @param glyph Glyph ID + * @param position Position relative to point on baseline + * @param rectangle Region in texture atlas + * + * You can obtain unused non-overlapping regions with @ref reserve(). + * You can't overwrite already inserted glyph, however you can reset + * glyph @cpp 0 @ce to some meaningful value. + * + * Glyph parameters are expected to be without padding. + * + * See also @ref setImage() to upload glyph image. + * @see @ref padding() + */ + void insert(UnsignedInt glyph, const Vector2i& position, const Range2Di& rectangle); + + /** + * @brief Set cache image + * + * Uploads image for one or more glyphs to given offset in cache + * texture. Calls @ref doSetImage(). The @p offset and + * @ref ImageView::size() are expected tro be in bounds for + * @ref textureSize(). + */ + void setImage(const Vector2i& offset, const ImageView2D& image); + + /** + * @brief Download cache image + * + * Downloads the cache texture back. Calls @ref doImage(). Available + * only if @ref GlyphCacheFeature::ImageDownload is supported. + * @see @ref features() + */ + Image2D image(); + + private: + /** @brief Implementation for @ref features() */ + virtual GlyphCacheFeatures doFeatures() const = 0; + + /** + * @brief Implementation for @ref setImage() + * + * The @p offset and @ref ImageView::size() are guaranteed to be in + * bounds for @ref textureSize(). + */ + virtual void doSetImage(const Vector2i& offset, const ImageView2D& image) = 0; + + /** @brief Implementation for @ref image() */ + virtual Image2D doImage(); + + Vector2i _size, _padding; + std::unordered_map> glyphs; +}; + +}} + +#endif diff --git a/src/Magnum/Text/CMakeLists.txt b/src/Magnum/Text/CMakeLists.txt index bf3afbff3..8cfa5bfd6 100644 --- a/src/Magnum/Text/CMakeLists.txt +++ b/src/Magnum/Text/CMakeLists.txt @@ -25,32 +25,62 @@ find_package(Corrade REQUIRED PluginManager) +# Files shared between main library and unit test library set(MagnumText_SRCS + AbstractFontConverter.cpp) + +# Files compiled with different flags for main library and unit test library +set(MagnumText_GracefulAssert_SRCS AbstractFont.cpp - AbstractFontConverter.cpp - DistanceFieldGlyphCache.cpp - GlyphCache.cpp - Renderer.cpp) + AbstractGlyphCache.cpp) + set(MagnumText_HEADERS AbstractFont.h AbstractFontConverter.h + AbstractGlyphCache.h Alignment.h - DistanceFieldGlyphCache.h - GlyphCache.h - Renderer.h Text.h visibility.h) +if(TARGET_GL) + list(APPEND MagnumText_SRCS + DistanceFieldGlyphCache.cpp + GlyphCache.cpp + Renderer.cpp) + list(APPEND MagnumText_HEADERS + DistanceFieldGlyphCache.h + GlyphCache.h + Renderer.h) +endif() + if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) endif() -# Text library -add_library(MagnumText ${SHARED_OR_STATIC} +# Objects shared between main and test library +add_library(MagnumTextObjects OBJECT ${MagnumText_SRCS} ${MagnumText_HEADERS}) +target_include_directories(MagnumTextObjects PUBLIC + $ + $) +if(TARGET_GL) + target_include_directories(MagnumTextObjects PUBLIC $) +endif() +if(NOT BUILD_STATIC) + target_compile_definitions(MagnumTextObjects PRIVATE "MagnumTextObjects_EXPORTS") +endif() +if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) + set_target_properties(MagnumTextObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +set_target_properties(MagnumTextObjects PROPERTIES FOLDER "Magnum/Text") + +# Text library +add_library(MagnumText ${SHARED_OR_STATIC} + $ + ${MagnumText_GracefulAssert_SRCS}) set_target_properties(MagnumText PROPERTIES DEBUG_POSTFIX "-d" FOLDER "Magnum/Text") @@ -61,9 +91,11 @@ elseif(BUILD_STATIC_PIC) endif() target_link_libraries(MagnumText PUBLIC Magnum - MagnumGL MagnumTextureTools Corrade::PluginManager) +if(TARGET_GL) + target_link_libraries(MagnumText PUBLIC MagnumGL) +endif() install(TARGETS MagnumText RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -107,6 +139,35 @@ if(WITH_FONTCONVERTER) endif() if(BUILD_TESTS) + # Library with graceful assert for testing + add_library(MagnumTextTestLib ${SHARED_OR_STATIC} + $ + ${MagnumText_GracefulAssert_SRCS}) + set_target_properties(MagnumTextTestLib PROPERTIES + DEBUG_POSTFIX "-d" + FOLDER "Magnum/Text") + target_compile_definitions(MagnumTextTestLib PRIVATE + "CORRADE_GRACEFUL_ASSERT" "MagnumText_EXPORTS") + if(BUILD_STATIC_PIC) + set_target_properties(MagnumTextTestLib PROPERTIES POSITION_INDEPENDENT_CODE ON) + endif() + target_link_libraries(MagnumTextTestLib PUBLIC + Magnum + MagnumTextureTools + Corrade::PluginManager) + if(TARGET_GL) + target_link_libraries(MagnumText PUBLIC MagnumGL) + endif() + + # On Windows we need to install first and then run the tests to avoid "DLL + # not found" hell, thus we need to install this too + if(CORRADE_TARGET_WINDOWS AND NOT CMAKE_CROSSCOMPILING AND NOT BUILD_STATIC) + install(TARGETS MagnumTextTestLib + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + endif() + add_subdirectory(Test) endif() diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.cpp b/src/Magnum/Text/DistanceFieldGlyphCache.cpp index c340f71ec..00ebe46de 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.cpp +++ b/src/Magnum/Text/DistanceFieldGlyphCache.cpp @@ -59,7 +59,7 @@ DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& originalSize, c #endif } -void DistanceFieldGlyphCache::setImage(const Vector2i& offset, const ImageView2D& image) { +void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView2D& image) { GL::Texture2D input; input.setWrapping(GL::SamplerWrapping::ClampToEdge) .setMinificationFilter(GL::SamplerFilter::Linear) diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.h b/src/Magnum/Text/DistanceFieldGlyphCache.h index 9908bb5fe..bd2889867 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.h +++ b/src/Magnum/Text/DistanceFieldGlyphCache.h @@ -29,6 +29,9 @@ * @brief Class @ref Magnum::Text::DistanceFieldGlyphCache */ +#include "Magnum/configure.h" + +#ifdef MAGNUM_TARGET_GL #include "Magnum/Text/GlyphCache.h" #include "Magnum/TextureTools/DistanceField.h" @@ -48,6 +51,10 @@ resulting distance field texture. @snippet MagnumText.cpp DistanceFieldGlyphCache-usage +@note This class is available only if Magnum is compiled with + @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features + for more information. + @see @ref TextureTools::distanceField() */ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { @@ -69,14 +76,6 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { */ explicit DistanceFieldGlyphCache(const Vector2i& originalSize, const Vector2i& size, UnsignedInt radius); - /** - * @brief Set cache image - * - * Uploads image for one or more glyphs to given offset in original - * cache texture. The texture is then converted to distance field. - */ - void setImage(const Vector2i& offset, const ImageView2D& image) override; - /** * @brief Set distance field cache image * @@ -86,10 +85,15 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { void setDistanceFieldImage(const Vector2i& offset, const ImageView2D& image); private: + void doSetImage(const Vector2i& offset, const ImageView2D& image) override; + Vector2 _scale; TextureTools::DistanceField _distanceField; }; }} +#else +#error this header is available only in the OpenGL build +#endif #endif diff --git a/src/Magnum/Text/GlyphCache.cpp b/src/Magnum/Text/GlyphCache.cpp index 39b39e103..4577d7383 100644 --- a/src/Magnum/Text/GlyphCache.cpp +++ b/src/Magnum/Text/GlyphCache.cpp @@ -26,6 +26,7 @@ #include "GlyphCache.h" #include "Magnum/Image.h" +#include "Magnum/PixelFormat.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/TextureFormat.h" @@ -35,60 +36,46 @@ namespace Magnum { namespace Text { GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding): GlyphCache{internalFormat, size, size, padding} {} -GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding): _size(originalSize), _padding(padding) { - initialize(internalFormat, size); +GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding): AbstractGlyphCache{originalSize, padding} { + /* Initialize the texture */ + _texture.setWrapping(GL::SamplerWrapping::ClampToEdge) + .setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setStorage(1, internalFormat, size); } GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): GlyphCache{size, size, padding} {} -GlyphCache::GlyphCache(const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding): _size(originalSize), _padding(padding) { - #ifndef MAGNUM_TARGET_GLES - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg); - #endif - +GlyphCache::GlyphCache(const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding): GlyphCache{ #ifndef MAGNUM_TARGET_GLES2 - const GL::TextureFormat internalFormat = GL::TextureFormat::R8; + GL::TextureFormat::R8, #else - const GL::TextureFormat internalFormat = GL::TextureFormat::Luminance; + GL::TextureFormat::Luminance, + #endif + originalSize, size, padding} +{ + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg); #endif - - initialize(internalFormat, size); } GlyphCache::~GlyphCache() = default; -void GlyphCache::initialize(const GL::TextureFormat internalFormat, const Vector2i& size) { - /* Initialize texture */ - _texture.setWrapping(GL::SamplerWrapping::ClampToEdge) - .setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setStorage(1, internalFormat, size); - - /* Default "Not Found" glyph. Can't do just `.insert({0, {}})` because - that's ambiguous in C++17, due to a new insert(node_type&&) overload. */ - glyphs.insert({0, std::pair{}}); -} - -std::vector GlyphCache::reserve(const std::vector& sizes) { - CORRADE_ASSERT((glyphs.size() == 1 && glyphs.at(0) == std::pair()), - "Text::GlyphCache::reserve(): reserving space in non-empty cache is not yet implemented", {}); - glyphs.reserve(glyphs.size() + sizes.size()); - return TextureTools::atlas(_size, sizes, _padding); -} - -void GlyphCache::insert(const UnsignedInt glyph, const Vector2i& position, const Range2Di& rectangle) { - const std::pair glyphData = {position-_padding, rectangle.padded(_padding)}; - - /* Overwriting "Not Found" glyph */ - if(glyph == 0) glyphs[0] = glyphData; - - /* Inserting new glyph */ - else CORRADE_INTERNAL_ASSERT_OUTPUT(glyphs.insert({glyph, glyphData}).second); +GlyphCacheFeatures GlyphCache::doFeatures() const { + #ifndef MAGNUM_TARGET_GLES + return GlyphCacheFeature::ImageDownload; + #else + return {}; + #endif } -void GlyphCache::setImage(const Vector2i& offset, const ImageView2D& image) { +void GlyphCache::doSetImage(const Vector2i& offset, const ImageView2D& image) { /** @todo some internalformat/format checking also here (if querying internal format is not slow) */ _texture.setSubImage(0, offset, image); } +#ifndef MAGNUM_TARGET_GLES +Image2D GlyphCache::doImage() { return _texture.image(0, PixelFormat::R8Unorm); } +#endif + }} diff --git a/src/Magnum/Text/GlyphCache.h b/src/Magnum/Text/GlyphCache.h index 63452b939..79e6eeb42 100644 --- a/src/Magnum/Text/GlyphCache.h +++ b/src/Magnum/Text/GlyphCache.h @@ -29,12 +29,11 @@ * @brief Class @ref Magnum::Text::GlyphCache */ -#include -#include +#include "Magnum/configure.h" -#include "Magnum/Math/Range.h" +#ifdef MAGNUM_TARGET_GL #include "Magnum/GL/Texture.h" -#include "Magnum/Text/visibility.h" +#include "Magnum/Text/AbstractGlyphCache.h" namespace Magnum { namespace Text { @@ -51,11 +50,20 @@ Create GlyphCache object with sufficient size and then call @snippet MagnumText.cpp GlyphCache-usage See @ref Renderer for information about text rendering. + +This class supports the @ref GlyphCacheFeature::ImageDownload (and thus calling +@ref image()) only on desktop OpenGL, due to using @ref GL::Texture::image(), +which is not available on @ref CORRADE_TARGET_GLES "OpenGL ES" platforms. + @todo Some way for Font to negotiate or check internal texture format @todo Default glyph 0 with rect 0 0 0 0 will result in negative dimensions when nonzero padding is removed + +@note This class is available only if Magnum is compiled with + @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features + for more information. */ -class MAGNUM_TEXT_EXPORT GlyphCache { +class MAGNUM_TEXT_EXPORT GlyphCache: public AbstractGlyphCache { public: /** * @brief Constructor @@ -98,103 +106,24 @@ class MAGNUM_TEXT_EXPORT GlyphCache { */ explicit GlyphCache(const Vector2i& size, const Vector2i& padding = {}); - virtual ~GlyphCache(); - - /** - * @brief Cache size - * - * Size of unscaled glyph cache texture. - */ - Vector2i textureSize() const { return _size; } - - /** @brief Glyph padding */ - Vector2i padding() const { return _padding; } - - /** @brief Count of glyphs in the cache */ - std::size_t glyphCount() const { return glyphs.size(); } + ~GlyphCache(); /** @brief Cache texture */ GL::Texture2D& texture() { return _texture; } - /** - * @brief Parameters of given glyph - * @param glyph Glyph ID - * - * First tuple element is glyph position relative to point on baseline, - * second element is glyph region in texture atlas. - * - * Returned values include padding. - * - * If no glyph is found, glyph @cpp 0 @ce is returned, which is by - * default on zero position and has zero region in texture atlas. You - * can reset it to some meaningful value in @ref insert(). - * @see @ref padding() - */ - std::pair operator[](UnsignedInt glyph) const { - auto it = glyphs.find(glyph); - return it == glyphs.end() ? glyphs.at(0) : it->second; - } - - /** @brief Iterator access to cache data */ - std::unordered_map>::const_iterator begin() const { - return glyphs.begin(); - } - - /** @brief Iterator access to cache data */ - std::unordered_map>::const_iterator end() const { - return glyphs.end(); - } - - /** - * @brief Layout glyphs with given sizes to the cache - * - * Returns non-overlapping regions in cache texture to store glyphs. - * The reserved space is reused on next call to @ref reserve() if no - * glyph was stored there, use @ref insert() to store actual glyph on - * given position and @ref setImage() to upload glyph image. - * - * Glyph @p sizes are expected to be without padding. - * - * @attention Cache size must be large enough to contain all rendered - * glyphs. - * @see @ref padding() - */ - std::vector reserve(const std::vector& sizes); - - /** - * @brief Insert glyph to cache - * @param glyph Glyph ID - * @param position Position relative to point on baseline - * @param rectangle Region in texture atlas - * - * You can obtain unused non-overlapping regions with @ref reserve(). - * You can't overwrite already inserted glyph, however you can reset - * glyph @cpp 0 @ce to some meaningful value. - * - * Glyph parameters are expected to be without padding. - * - * See also @ref setImage() to upload glyph image. - * @see @ref padding() - */ - void insert(UnsignedInt glyph, const Vector2i& position, const Range2Di& rectangle); - - /** - * @brief Set cache image - * - * Uploads image for one or more glyphs to given offset in cache - * texture. - */ - virtual void setImage(const Vector2i& offset, const ImageView2D& image); - private: - void MAGNUM_LOCAL initialize(GL::TextureFormat internalFormat, const Vector2i& size); + GlyphCacheFeatures MAGNUM_LOCAL doFeatures() const override; + void MAGNUM_LOCAL doSetImage(const Vector2i& offset, const ImageView2D& image) override; + #ifndef MAGNUM_TARGET_GLES + Image2D MAGNUM_LOCAL doImage() override; + #endif - Vector2i _size, _padding; GL::Texture2D _texture; - - std::unordered_map> glyphs; }; }} +#else +#error this header is available only in the OpenGL build +#endif #endif diff --git a/src/Magnum/Text/Renderer.cpp b/src/Magnum/Text/Renderer.cpp index 7bfb1b56e..fac3de231 100644 --- a/src/Magnum/Text/Renderer.cpp +++ b/src/Magnum/Text/Renderer.cpp @@ -34,6 +34,7 @@ #include "Magnum/Math/Functions.h" #include "Magnum/Shaders/AbstractVector.h" #include "Magnum/Text/AbstractFont.h" +#include "Magnum/Text/GlyphCache.h" namespace Magnum { namespace Text { diff --git a/src/Magnum/Text/Renderer.h b/src/Magnum/Text/Renderer.h index aa2904956..fce977a53 100644 --- a/src/Magnum/Text/Renderer.h +++ b/src/Magnum/Text/Renderer.h @@ -29,6 +29,9 @@ * @brief Class @ref Magnum::Text::AbstractRenderer, @ref Magnum::Text::Renderer, typedef @ref Magnum::Text::Renderer2D, @ref Magnum::Text::Renderer3D */ +#include "Magnum/configure.h" + +#ifdef MAGNUM_TARGET_GL #include #include #include @@ -241,5 +244,8 @@ typedef Renderer<2> Renderer2D; typedef Renderer<3> Renderer3D; }} +#else +#error this header is available only in the OpenGL build +#endif #endif diff --git a/src/Magnum/Text/Test/AbstractFontConverterTest.cpp b/src/Magnum/Text/Test/AbstractFontConverterTest.cpp index 291c80be7..70d8f3c71 100644 --- a/src/Magnum/Text/Test/AbstractFontConverterTest.cpp +++ b/src/Magnum/Text/Test/AbstractFontConverterTest.cpp @@ -29,8 +29,9 @@ #include #include +#include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFontConverter.h" -#include "Magnum/Text/GlyphCache.h" +#include "Magnum/Text/AbstractGlyphCache.h" #include "configure.h" @@ -67,10 +68,24 @@ AbstractFontConverterTest::AbstractFontConverterTest() { Utility::Directory::mkpath(TEXT_TEST_OUTPUT_DIR); } -/* *static_cast(nullptr) makes Clang Analyzer grumpy */ -char nullData; -AbstractFont& nullFont = *reinterpret_cast(nullData); -GlyphCache& nullGlyphCache = *reinterpret_cast(nullData); +struct DummyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } +} dummyFont; + +struct DummyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} +} dummyGlyphCache{{}}; void AbstractFontConverterTest::convertGlyphs() { class GlyphExporter: public AbstractFontConverter { @@ -81,7 +96,7 @@ void AbstractFontConverterTest::convertGlyphs() { private: Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont; } - Containers::Array doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string& characters) const override { + Containers::Array doExportFontToSingleData(AbstractFont&, AbstractGlyphCache&, const std::u32string& characters) const override { _characters = characters; return nullptr; } @@ -91,7 +106,7 @@ void AbstractFontConverterTest::convertGlyphs() { std::u32string characters; GlyphExporter exporter(characters); - exporter.exportFontToSingleData(nullFont, nullGlyphCache, "abC01a0 "); + exporter.exportFontToSingleData(dummyFont, dummyGlyphCache, "abC01a0 "); CORRADE_COMPARE(characters, U" 01Cab"); } @@ -100,7 +115,7 @@ void AbstractFontConverterTest::exportFontToSingleData() { private: Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont; } - Containers::Array doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string&) const override { + Containers::Array doExportFontToSingleData(AbstractFont&, AbstractGlyphCache&, const std::u32string&) const override { Containers::Array data(1); data[0] = '\xee'; return data; @@ -109,7 +124,7 @@ void AbstractFontConverterTest::exportFontToSingleData() { /* doExportFontToData() should call doExportFontToSingleData() */ SingleDataExporter exporter; - auto ret = exporter.exportFontToData(nullFont, nullGlyphCache, "font.out", {}); + auto ret = exporter.exportFontToData(dummyFont, dummyGlyphCache, "font.out", {}); CORRADE_COMPARE(ret.size(), 1); CORRADE_COMPARE(ret[0].first, "font.out"); CORRADE_COMPARE(ret[0].second.size(), 1); @@ -121,7 +136,7 @@ void AbstractFontConverterTest::exportFontToFile() { private: Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont|Feature::MultiFile; } - std::vector>> doExportFontToData(AbstractFont&, GlyphCache&, const std::string& filename, const std::u32string&) const override { + std::vector>> doExportFontToData(AbstractFont&, AbstractGlyphCache&, const std::string& filename, const std::u32string&) const override { /* Why the hell GCC 4.9 fails to do proper move so I need to work around that this ugly way?! */ std::vector>> ret; @@ -137,7 +152,7 @@ void AbstractFontConverterTest::exportFontToFile() { /* doExportToFile() should call doExportToData() */ DataExporter exporter; - bool exported = exporter.exportFontToFile(nullFont, nullGlyphCache, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), {}); + bool exported = exporter.exportFontToFile(dummyFont, dummyGlyphCache, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), {}); CORRADE_VERIFY(exported); CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), "\xf0", TestSuite::Compare::FileToString); @@ -146,18 +161,16 @@ void AbstractFontConverterTest::exportFontToFile() { } void AbstractFontConverterTest::exportGlyphCacheToSingleData() { - class SingleDataExporter: public Text::AbstractFontConverter { - private: - Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache; } + struct: Text::AbstractFontConverter { + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache; } - Containers::Array doExportGlyphCacheToSingleData(GlyphCache&) const override { - return Containers::Array{Containers::InPlaceInit, {'\xee'}}; - } - }; + Containers::Array doExportGlyphCacheToSingleData(AbstractGlyphCache&) const override { + return Containers::Array{Containers::InPlaceInit, {'\xee'}}; + } + } exporter; /* doExportGlyphCacheToData() should call doExportGlyphCacheToSingleData() */ - SingleDataExporter exporter; - auto ret = exporter.exportGlyphCacheToData(nullGlyphCache, "font.out"); + auto ret = exporter.exportGlyphCacheToData(dummyGlyphCache, "font.out"); CORRADE_COMPARE(ret.size(), 1); CORRADE_COMPARE(ret[0].first, "font.out"); CORRADE_COMPARE_AS(ret[0].second, @@ -170,7 +183,7 @@ void AbstractFontConverterTest::exportGlyphCacheToFile() { private: Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache|Feature::MultiFile; } - std::vector>> doExportGlyphCacheToData(GlyphCache&, const std::string& filename) const override { + std::vector>> doExportGlyphCacheToData(AbstractGlyphCache&, const std::string& filename) const override { /* Why the hell GCC 4.9 fails to do proper move so I need to work around that this ugly way?! */ std::vector>> ret; @@ -186,7 +199,7 @@ void AbstractFontConverterTest::exportGlyphCacheToFile() { /* doExportGlyphCacheToFile() should call doExportGlyphCacheToData() */ DataExporter exporter; - bool exported = exporter.exportGlyphCacheToFile(nullGlyphCache, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); + bool exported = exporter.exportGlyphCacheToFile(dummyGlyphCache, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); CORRADE_VERIFY(exported); CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out"), "\xf0", TestSuite::Compare::FileToString); @@ -198,9 +211,9 @@ class SingleGlyphCacheDataImporter: public Text::AbstractFontConverter { private: Features doFeatures() const override { return Feature::ConvertData|Feature::ImportGlyphCache; } - Containers::Pointer doImportGlyphCacheFromSingleData(const Containers::ArrayView data) const override { + Containers::Pointer doImportGlyphCacheFromSingleData(const Containers::ArrayView data) const override { if(data.size() == 1 && data[0] == '\xa5') - return Containers::Pointer(reinterpret_cast(0xdeadbeef)); + return Containers::pointer(new DummyGlyphCache{{123, 345}}); return nullptr; } }; @@ -209,21 +222,15 @@ void AbstractFontConverterTest::importGlyphCacheFromSingleData() { /* doImportFromData() should call doImportFromSingleData() */ SingleGlyphCacheDataImporter importer; const char data[] = {'\xa5'}; - Containers::Pointer cache = importer.importGlyphCacheFromData({{{}, data}}); - CORRADE_COMPARE(cache.get(), reinterpret_cast(0xdeadbeef)); - - /* The pointer is invalid, avoid deletion */ - cache.release(); + Containers::Pointer cache = importer.importGlyphCacheFromData({{{}, data}}); + CORRADE_COMPARE(cache->textureSize(), (Vector2i{123, 345})); } void AbstractFontConverterTest::importGlyphCacheFromFile() { /* doImportFromFile() should call doImportFromSingleData() */ SingleGlyphCacheDataImporter importer; - Containers::Pointer cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin")); - CORRADE_COMPARE(cache.get(), reinterpret_cast(0xdeadbeef)); - - /* The pointer is invalid, avoid deletion */ - cache.release(); + Containers::Pointer cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin")); + CORRADE_COMPARE(cache->textureSize(), (Vector2i{123, 345})); } }}}} diff --git a/src/Magnum/Text/Test/AbstractFontTest.cpp b/src/Magnum/Text/Test/AbstractFontTest.cpp index 03aa9c0f0..4e9adc472 100644 --- a/src/Magnum/Text/Test/AbstractFontTest.cpp +++ b/src/Magnum/Text/Test/AbstractFontTest.cpp @@ -23,12 +23,14 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include #include "Magnum/Math/Vector2.h" #include "Magnum/Text/AbstractFont.h" +#include "Magnum/Text/AbstractGlyphCache.h" #include "configure.h" @@ -38,12 +40,62 @@ struct AbstractFontTest: TestSuite::Tester { explicit AbstractFontTest(); void openSingleData(); - void openFile(); + + void openFileAsData(); + void openFileAsDataNotFound(); + + void openFileNotImplemented(); + void openDataNotSupported(); + void openDataNotImplemented(); + + void glyphId(); + void glyphIdNoFont(); + + void glyphAdvance(); + void glyphAdvanceNoFont(); + + void layout(); + void layoutNoFont(); + + void fillGlyphCache(); + void fillGlyphCacheNotSupported(); + void fillGlyphCacheNotImplemented(); + void fillGlyphCacheNoFont(); + + void createGlyphCache(); + void createGlyphCacheNotSupported(); + void createGlyphCacheNotImplemented(); + void createGlyphCacheNoFont(); }; AbstractFontTest::AbstractFontTest() { addTests({&AbstractFontTest::openSingleData, - &AbstractFontTest::openFile}); + + &AbstractFontTest::openFileAsData, + &AbstractFontTest::openFileAsDataNotFound, + + &AbstractFontTest::openFileNotImplemented, + &AbstractFontTest::openDataNotSupported, + &AbstractFontTest::openDataNotImplemented, + + &AbstractFontTest::glyphId, + &AbstractFontTest::glyphIdNoFont, + + &AbstractFontTest::glyphAdvance, + &AbstractFontTest::glyphAdvanceNoFont, + + &AbstractFontTest::layout, + &AbstractFontTest::layoutNoFont, + + &AbstractFontTest::fillGlyphCache, + &AbstractFontTest::fillGlyphCacheNotSupported, + &AbstractFontTest::fillGlyphCacheNotImplemented, + &AbstractFontTest::fillGlyphCacheNoFont, + + &AbstractFontTest::createGlyphCache, + &AbstractFontTest::createGlyphCacheNotSupported, + &AbstractFontTest::createGlyphCacheNotImplemented, + &AbstractFontTest::createGlyphCacheNoFont}); } class SingleDataFont: public Text::AbstractFont { @@ -63,7 +115,7 @@ class SingleDataFont: public Text::AbstractFont { Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } - Containers::Pointer doLayout(const GlyphCache&, Float, const std::string&) override { + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } @@ -79,7 +131,7 @@ void AbstractFontTest::openSingleData() { CORRADE_VERIFY(font.isOpened()); } -void AbstractFontTest::openFile() { +void AbstractFontTest::openFileAsData() { /* doOpenFile() should call doOpenSingleData() */ SingleDataFont font; CORRADE_VERIFY(!font.isOpened()); @@ -87,6 +139,364 @@ void AbstractFontTest::openFile() { CORRADE_VERIFY(font.isOpened()); } +void AbstractFontTest::openFileAsDataNotFound() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::OpenData; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!font.openFile("nonexistent.foo", 12.0f)); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::openFile(): cannot open file nonexistent.foo\n"); +} + +void AbstractFontTest::openFileNotImplemented() { + struct MyFont: AbstractFont { + /* Supports neither file nor data opening */ + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.openFile("file.foo", 34.0f); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::openFile(): not implemented\n"); +} + +void AbstractFontTest::openDataNotSupported() { + struct MyFont: AbstractFont { + /* Supports neither file nor data opening */ + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + /** @todo replace this with nullptr once multi-file support is done + properly via callbacks */ + font.openData({}, 34.0f); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::openData(): feature not supported\n"); +} + +void AbstractFontTest::openDataNotImplemented() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::OpenData; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + /** @todo replace this with nullptr and openSingleData() once multi-file + support is done properly via callbacks */ + font.openData({{}}, 34.0f); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::openSingleData(): feature advertised but not implemented\n"); +} + +void AbstractFontTest::glyphId() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t a) override { return a*10; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + CORRADE_COMPARE(font.glyphId(U'a'), 970); +} + +void AbstractFontTest::glyphIdNoFont() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.glyphId('a'); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::glyphId(): no font opened\n"); +} + +void AbstractFontTest::glyphAdvance() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt a) override { return {a*10.0f, -Float(a)/10.0f}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + CORRADE_COMPARE(font.glyphAdvance(97), (Vector2{970.0f, -9.7f})); +} + +void AbstractFontTest::glyphAdvanceNoFont() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { + return nullptr; + } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.glyphAdvance(97); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::glyphAdvance(): no font opened\n"); +} + +struct DummyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} +}; + +void AbstractFontTest::layout() { + struct Layouter: AbstractLayouter { + explicit Layouter(UnsignedInt count): AbstractLayouter{count} {} + std::tuple doRenderGlyph(UnsignedInt) override { return {}; } + }; + + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache& cache, Float size, const std::string& str) override { + return Containers::pointer(UnsignedInt(cache.textureSize().x()*str.size()*size)); + } + } font; + + DummyGlyphCache cache{{100, 200}}; + Containers::Pointer layouter = font.layout(cache, 0.25f, "hello"); + CORRADE_COMPARE(layouter->glyphCount(), 100*5/4); +} + +void AbstractFontTest::layoutNoFont() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + DummyGlyphCache cache{{100, 200}}; + font.layout(cache, 0.25f, "hello"); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::layout(): no font opened\n"); +} + +void AbstractFontTest::fillGlyphCache() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + + void doFillGlyphCache(AbstractGlyphCache& cache, const std::u32string& characters) override { + for(char a: characters) cache.insert(a*10, {a/2, a*2}, {}); + } + } font; + + DummyGlyphCache cache{{100, 100}}; + + CORRADE_COMPARE(cache.glyphCount(), 1); + font.fillGlyphCache(cache, "helo"); + + CORRADE_COMPARE(cache.glyphCount(), 5); + CORRADE_COMPARE(cache['h'*10], (std::pair{{52, 208}, {}})); + CORRADE_COMPARE(cache['e'*10], (std::pair{{50, 202}, {}})); + CORRADE_COMPARE(cache['l'*10], (std::pair{{54, 216}, {}})); + CORRADE_COMPARE(cache['o'*10], (std::pair{{55, 222}, {}})); +} + +void AbstractFontTest::fillGlyphCacheNotSupported() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::PreparedGlyphCache; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + DummyGlyphCache cache{{100, 100}}; + font.fillGlyphCache(cache, "hello"); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::fillGlyphCache(): feature not supported\n"); +} + +void AbstractFontTest::fillGlyphCacheNotImplemented() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + DummyGlyphCache cache{{100, 100}}; + font.fillGlyphCache(cache, "hello"); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::fillGlyphCache(): feature advertised but not implemented\n"); +} + +void AbstractFontTest::fillGlyphCacheNoFont() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + DummyGlyphCache cache{{100, 100}}; + font.fillGlyphCache(cache, "hello"); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::fillGlyphCache(): no font opened\n"); +} + +void AbstractFontTest::createGlyphCache() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::PreparedGlyphCache; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + + Containers::Pointer doCreateGlyphCache() override { + Containers::Pointer cache{new DummyGlyphCache{{100, 100}}}; + for(char a: std::string{"helo"}) cache->insert(a*10, {a/2, a*2}, {}); + return cache; + } + } font; + + Containers::Pointer cache = font.createGlyphCache(); + + CORRADE_COMPARE(cache->glyphCount(), 5); + CORRADE_COMPARE((*cache)['h'*10], (std::pair{{52, 208}, {}})); + CORRADE_COMPARE((*cache)['e'*10], (std::pair{{50, 202}, {}})); + CORRADE_COMPARE((*cache)['l'*10], (std::pair{{54, 216}, {}})); + CORRADE_COMPARE((*cache)['o'*10], (std::pair{{55, 222}, {}})); +} + +void AbstractFontTest::createGlyphCacheNotSupported() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.createGlyphCache(); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::createGlyphCache(): feature not supported\n"); +} + +void AbstractFontTest::createGlyphCacheNotImplemented() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::PreparedGlyphCache; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.createGlyphCache(); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::createGlyphCache(): feature advertised but not implemented\n"); +} + +void AbstractFontTest::createGlyphCacheNoFont() { + struct MyFont: AbstractFont { + Features doFeatures() const override { return Feature::PreparedGlyphCache; } + bool doIsOpened() const override { return false; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return {}; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; } + } font; + + std::ostringstream out; + Error redirectError{&out}; + font.createGlyphCache(); + CORRADE_COMPARE(out.str(), "Text::AbstractFont::createGlyphCache(): no font opened\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontTest) diff --git a/src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp b/src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp new file mode 100644 index 000000000..51ae4cd06 --- /dev/null +++ b/src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp @@ -0,0 +1,192 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/Image.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/Text/AbstractGlyphCache.h" + +namespace Magnum { namespace Text { namespace Test { namespace { + +struct AbstractGlyphCacheTest: TestSuite::Tester { + explicit AbstractGlyphCacheTest(); + + void initialize(); + void access(); + void reserve(); + + void setImage(); + void setImageOutOfBounds(); + + void image(); + void imageNotSupported(); + void imageNotImplemented(); +}; + +AbstractGlyphCacheTest::AbstractGlyphCacheTest() { + addTests({&AbstractGlyphCacheTest::initialize, + &AbstractGlyphCacheTest::access, + &AbstractGlyphCacheTest::reserve, + + &AbstractGlyphCacheTest::setImage, + &AbstractGlyphCacheTest::setImageOutOfBounds, + + &AbstractGlyphCacheTest::image, + &AbstractGlyphCacheTest::imageNotSupported, + &AbstractGlyphCacheTest::imageNotImplemented}); +} + +struct DummyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} +}; + +void AbstractGlyphCacheTest::initialize() { + DummyGlyphCache cache({1024, 2048}); + + CORRADE_COMPARE(cache.textureSize(), (Vector2i{1024, 2048})); +} + +void AbstractGlyphCacheTest::access() { + DummyGlyphCache cache(Vector2i(236)); + Vector2i position; + Range2Di rectangle; + + /* Default "Not Found" glyph */ + CORRADE_COMPARE(cache.glyphCount(), 1); + std::tie(position, rectangle) = cache[0]; + CORRADE_COMPARE(position, Vector2i(0, 0)); + CORRADE_COMPARE(rectangle, Range2Di({0, 0}, {0, 0})); + + /* Overwrite the "Not Found" glyph */ + cache.insert(0, {3, 5}, {{10, 10}, {23, 45}}); + CORRADE_COMPARE(cache.glyphCount(), 1); + std::tie(position, rectangle) = cache[0]; + CORRADE_COMPARE(position, Vector2i(3, 5)); + CORRADE_COMPARE(rectangle, Range2Di({10, 10}, {23, 45})); + + /* Querying available glyph */ + cache.insert(25, {3, 4}, {{15, 30}, {45, 35}}); + CORRADE_COMPARE(cache.glyphCount(), 2); + std::tie(position, rectangle) = cache[25]; + CORRADE_COMPARE(position, Vector2i(3, 4)); + CORRADE_COMPARE(rectangle, Range2Di({15, 30}, {45, 35})); + + /* Querying not available glyph falls back to "Not Found" */ + std::tie(position, rectangle) = cache[42]; + CORRADE_COMPARE(position, Vector2i(3, 5)); + CORRADE_COMPARE(rectangle, Range2Di({10, 10}, {23, 45})); +} + +void AbstractGlyphCacheTest::reserve() { + DummyGlyphCache cache(Vector2i(236)); + + /* Verify that this works for "empty" cache */ + CORRADE_VERIFY(!cache.reserve({{5, 3}}).empty()); +} + +void AbstractGlyphCacheTest::setImage() { + struct MyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i& offset, const ImageView2D& image) override { + this->offset = offset; + this->size = image.size(); + } + + Vector2i offset, size; + } cache{{100, 200}}; + + cache.setImage({80, 175}, ImageView2D{{}, {20, 25}, nullptr}); + + CORRADE_COMPARE(cache.offset, (Vector2i{80, 175})); + CORRADE_COMPARE(cache.size, (Vector2i{20, 25})); +} + +void AbstractGlyphCacheTest::setImageOutOfBounds() { + DummyGlyphCache cache{{100, 200}}; + + std::ostringstream out; + Error redirectError{&out}; + cache.setImage({80, 175}, ImageView2D{{}, {20, 25}, nullptr}); + cache.setImage({81, 175}, ImageView2D{{}, {20, 25}, nullptr}); + cache.setImage({80, -1}, ImageView2D{{}, {20, 25}, nullptr}); + + CORRADE_COMPARE(out.str(), + "Text::AbstractGlyphCache::setImage(): Range({81, 175}, {101, 200}) out of bounds for texture size Vector(100, 200)\n" + "Text::AbstractGlyphCache::setImage(): Range({80, -1}, {100, 24}) out of bounds for texture size Vector(100, 200)\n"); +} + +void AbstractGlyphCacheTest::image() { + struct MyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + + Image2D doImage() override { return Image2D{PixelFormat::RG8Unorm}; } + } cache{{200, 300}}; + + Image2D image = cache.image(); + CORRADE_COMPARE(image.format(), PixelFormat::RG8Unorm); +} + +void AbstractGlyphCacheTest::imageNotSupported() { + struct MyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } cache{{200, 300}}; + + std::ostringstream out; + Error redirectError{&out}; + cache.image(); + CORRADE_COMPARE(out.str(), "Text::AbstractGlyphCache::image(): feature not supported\n"); +} + +void AbstractGlyphCacheTest::imageNotImplemented() { + struct MyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } cache{{200, 300}}; + + std::ostringstream out; + Error redirectError{&out}; + cache.image(); + CORRADE_COMPARE(out.str(), "Text::AbstractGlyphCache::image(): feature advertised but not implemented\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractGlyphCacheTest) diff --git a/src/Magnum/Text/Test/CMakeLists.txt b/src/Magnum/Text/Test/CMakeLists.txt index ca4c0dd1e..dc43ca6d7 100644 --- a/src/Magnum/Text/Test/CMakeLists.txt +++ b/src/Magnum/Text/Test/CMakeLists.txt @@ -35,27 +35,30 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp - LIBRARIES Magnum MagnumText + LIBRARIES Magnum MagnumTextTestLib FILES data.bin) target_include_directories(TextAbstractFontTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIBRARIES Magnum MagnumText FILES data.bin) target_include_directories(TextAbstractFontConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +corrade_add_test(TextAbstractGlyphCacheTest AbstractGlyphCacheTest.cpp LIBRARIES MagnumTextTestLib) corrade_add_test(TextAbstractLayouterTest AbstractLayouterTest.cpp LIBRARIES Magnum MagnumText) set_target_properties( TextAbstractFontTest TextAbstractFontConverterTest + TextAbstractGlyphCacheTest TextAbstractLayouterTest PROPERTIES FOLDER "Magnum/Text/Test") -if(BUILD_GL_TESTS) +if(TARGET_GL AND BUILD_GL_TESTS) corrade_add_test(TextDistanceFieldGlyphCacheGLTest DistanceFieldGlyphCacheGLTest.cpp LIBRARIES MagnumText MagnumOpenGLTester) corrade_add_test(TextGlyphCacheGLTest GlyphCacheGLTest.cpp LIBRARIES MagnumText MagnumOpenGLTester) corrade_add_test(TextRendererGLTest RendererGLTest.cpp LIBRARIES MagnumText MagnumOpenGLTester) set_target_properties( + TextDistanceFieldGlyphCacheGLTest TextGlyphCacheGLTest TextRendererGLTest PROPERTIES FOLDER "Magnum/Text/Test") diff --git a/src/Magnum/Text/Test/GlyphCacheGLTest.cpp b/src/Magnum/Text/Test/GlyphCacheGLTest.cpp index d986b4adb..a50913649 100644 --- a/src/Magnum/Text/Test/GlyphCacheGLTest.cpp +++ b/src/Magnum/Text/Test/GlyphCacheGLTest.cpp @@ -34,14 +34,10 @@ struct GlyphCacheGLTest: GL::OpenGLTester { explicit GlyphCacheGLTest(); void initialize(); - void access(); - void reserve(); }; GlyphCacheGLTest::GlyphCacheGLTest() { - addTests({&GlyphCacheGLTest::initialize, - &GlyphCacheGLTest::access, - &GlyphCacheGLTest::reserve}); + addTests({&GlyphCacheGLTest::initialize}); } void GlyphCacheGLTest::initialize() { @@ -53,44 +49,6 @@ void GlyphCacheGLTest::initialize() { #endif } -void GlyphCacheGLTest::access() { - Text::GlyphCache cache(Vector2i(236)); - Vector2i position; - Range2Di rectangle; - - /* Default "Not Found" glyph */ - CORRADE_COMPARE(cache.glyphCount(), 1); - std::tie(position, rectangle) = cache[0]; - CORRADE_COMPARE(position, Vector2i(0, 0)); - CORRADE_COMPARE(rectangle, Range2Di({0, 0}, {0, 0})); - - /* Overwrite "Not Found" glyph */ - cache.insert(0, {3, 5}, {{10, 10}, {23, 45}}); - CORRADE_COMPARE(cache.glyphCount(), 1); - std::tie(position, rectangle) = cache[0]; - CORRADE_COMPARE(position, Vector2i(3, 5)); - CORRADE_COMPARE(rectangle, Range2Di({10, 10}, {23, 45})); - - /* Querying available glyph */ - cache.insert(25, {3, 4}, {{15, 30}, {45, 35}}); - CORRADE_COMPARE(cache.glyphCount(), 2); - std::tie(position, rectangle) = cache[25]; - CORRADE_COMPARE(position, Vector2i(3, 4)); - CORRADE_COMPARE(rectangle, Range2Di({15, 30}, {45, 35})); - - /* Querying not available glyph falls back to "Not Found" */ - std::tie(position, rectangle) = cache[42]; - CORRADE_COMPARE(position, Vector2i(3, 5)); - CORRADE_COMPARE(rectangle, Range2Di({10, 10}, {23, 45})); -} - -void GlyphCacheGLTest::reserve() { - Text::GlyphCache cache(Vector2i(236)); - - /* Verify that this works for "empty" cache */ - CORRADE_VERIFY(!cache.reserve({{5, 3}}).empty()); -} - }}}} CORRADE_TEST_MAIN(Magnum::Text::Test::GlyphCacheGLTest) diff --git a/src/Magnum/Text/Test/RendererGLTest.cpp b/src/Magnum/Text/Test/RendererGLTest.cpp index 4bda2aa5c..001d4ea76 100644 --- a/src/Magnum/Text/Test/RendererGLTest.cpp +++ b/src/Magnum/Text/Test/RendererGLTest.cpp @@ -79,7 +79,7 @@ class TestFont: public Text::AbstractFont { UnsignedInt doGlyphId(char32_t) override { return 0; } Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } - Containers::Pointer doLayout(const GlyphCache&, const Float size, const std::string& text) override { + Containers::Pointer doLayout(const AbstractGlyphCache&, const Float size, const std::string& text) override { return Containers::Pointer(new TestLayouter(size, text.size())); } }; @@ -353,7 +353,7 @@ void RendererGLTest::multiline() { UnsignedInt doGlyphId(char32_t) override { return 0; } Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } - Containers::Pointer doLayout(const GlyphCache&, Float, const std::string& text) override { + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string& text) override { return Containers::Pointer(new Layouter(text.size())); } diff --git a/src/Magnum/Text/Text.h b/src/Magnum/Text/Text.h index 2c5ee57fb..2210d4a18 100644 --- a/src/Magnum/Text/Text.h +++ b/src/Magnum/Text/Text.h @@ -37,17 +37,21 @@ namespace Magnum { namespace Text { #ifndef DOXYGEN_GENERATING_OUTPUT class AbstractFont; class AbstractFontConverter; +class AbstractGlyphCache; class AbstractLayouter; -class DistanceFieldGlyphCache; -class GlyphCache; enum class Alignment: UnsignedByte; +class AbstractGlyphCache; +#ifdef MAGNUM_TARGET_GL +class DistanceFieldGlyphCache; +class GlyphCache; class AbstractRenderer; template class Renderer; typedef Renderer<2> Renderer2D; typedef Renderer<3> Renderer3D; #endif +#endif }} diff --git a/src/Magnum/Text/visibility.h b/src/Magnum/Text/visibility.h index b1256c05e..8b48ba151 100644 --- a/src/Magnum/Text/visibility.h +++ b/src/Magnum/Text/visibility.h @@ -31,7 +31,7 @@ #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_BUILD_STATIC - #ifdef MagnumText_EXPORTS + #if defined(MagnumText_EXPORTS) || defined(MagnumTextObjects_EXPORTS) #define MAGNUM_TEXT_EXPORT CORRADE_VISIBILITY_EXPORT #else #define MAGNUM_TEXT_EXPORT CORRADE_VISIBILITY_IMPORT diff --git a/src/MagnumPlugins/MagnumFont/CMakeLists.txt b/src/MagnumPlugins/MagnumFont/CMakeLists.txt index 5e48bb20c..1acdabe19 100644 --- a/src/MagnumPlugins/MagnumFont/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFont/CMakeLists.txt @@ -56,7 +56,7 @@ if(BUILD_PLUGINS_STATIC) target_sources(MagnumFont INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) endif() -if(BUILD_GL_TESTS) +if(BUILD_TESTS) add_subdirectory(Test) endif() diff --git a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp index 24be1a41a..3e2580b0d 100644 --- a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp +++ b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp @@ -48,13 +48,13 @@ struct MagnumFont::Data { namespace { class MagnumFontLayouter: public AbstractLayouter { public: - explicit MagnumFontLayouter(const std::vector& glyphAdvance, const GlyphCache& cache, Float fontSize, Float textSize, std::vector&& glyphs); + explicit MagnumFontLayouter(const std::vector& glyphAdvance, const AbstractGlyphCache& cache, Float fontSize, Float textSize, std::vector&& glyphs); private: std::tuple doRenderGlyph(UnsignedInt i) override; const std::vector& glyphAdvance; - const GlyphCache& cache; + const AbstractGlyphCache& cache; const Float fontSize, textSize; const std::vector glyphs; }; @@ -184,9 +184,9 @@ Vector2 MagnumFont::doGlyphAdvance(const UnsignedInt glyph) { return glyph < _opened->glyphAdvance.size() ? _opened->glyphAdvance[glyph] : Vector2(); } -Containers::Pointer MagnumFont::doCreateGlyphCache() { +Containers::Pointer MagnumFont::doCreateGlyphCache() { /* Set cache image */ - Containers::Pointer cache(new Text::GlyphCache( + Containers::Pointer cache(new Text::GlyphCache( _opened->conf.value("originalImageSize"), _opened->image.size(), _opened->conf.value("padding"))); @@ -200,7 +200,7 @@ Containers::Pointer MagnumFont::doCreateGlyphCache() { return cache; } -Containers::Pointer MagnumFont::doLayout(const GlyphCache& cache, Float size, const std::string& text) { +Containers::Pointer MagnumFont::doLayout(const AbstractGlyphCache& cache, Float size, const std::string& text) { /* Get glyph codes from characters */ std::vector glyphs; glyphs.reserve(text.size()); @@ -216,7 +216,7 @@ Containers::Pointer MagnumFont::doLayout(const GlyphCache& cac namespace { -MagnumFontLayouter::MagnumFontLayouter(const std::vector& glyphAdvance, const GlyphCache& cache, const Float fontSize, const Float textSize, std::vector&& glyphs): AbstractLayouter(glyphs.size()), glyphAdvance(glyphAdvance), cache(cache), fontSize(fontSize), textSize(textSize), glyphs(std::move(glyphs)) {} +MagnumFontLayouter::MagnumFontLayouter(const std::vector& glyphAdvance, const AbstractGlyphCache& cache, const Float fontSize, const Float textSize, std::vector&& glyphs): AbstractLayouter(glyphs.size()), glyphAdvance(glyphAdvance), cache(cache), fontSize(fontSize), textSize(textSize), glyphs(std::move(glyphs)) {} std::tuple MagnumFontLayouter::doRenderGlyph(const UnsignedInt i) { /* Position of the texture in the resulting glyph, texture coordinates */ @@ -242,4 +242,4 @@ std::tuple MagnumFontLayouter::doRenderGlyph(const Un }} CORRADE_PLUGIN_REGISTER(MagnumFont, Magnum::Text::MagnumFont, - "cz.mosra.magnum.Text.AbstractFont/0.2.4") + "cz.mosra.magnum.Text.AbstractFont/0.3") diff --git a/src/MagnumPlugins/MagnumFont/MagnumFont.h b/src/MagnumPlugins/MagnumFont/MagnumFont.h index 0a20d4f3a..58bb906a3 100644 --- a/src/MagnumPlugins/MagnumFont/MagnumFont.h +++ b/src/MagnumPlugins/MagnumFont/MagnumFont.h @@ -29,6 +29,9 @@ * @brief Class @ref Magnum::Text::MagnumFont */ +#include "Magnum/configure.h" + +#ifdef MAGNUM_TARGET_GL #include "Magnum/Text/AbstractFont.h" #include "Magnum/Trade/Trade.h" @@ -143,8 +146,8 @@ class MAGNUM_MAGNUMFONT_EXPORT MagnumFont: public AbstractFont { MAGNUM_MAGNUMFONT_LOCAL UnsignedInt doGlyphId(char32_t character) override; MAGNUM_MAGNUMFONT_LOCAL Vector2 doGlyphAdvance(UnsignedInt glyph) override; - MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer doCreateGlyphCache() override; - MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer doLayout(const GlyphCache& cache, Float size, const std::string& text) override; + MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer doCreateGlyphCache() override; + MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer doLayout(const AbstractGlyphCache& cache, Float size, const std::string& text) override; MAGNUM_MAGNUMFONT_LOCAL Metrics openInternal(Utility::Configuration&& conf, Trade::ImageData2D&& image); @@ -152,5 +155,8 @@ class MAGNUM_MAGNUMFONT_EXPORT MagnumFont: public AbstractFont { }; }} +#else +#error this header is available only in the OpenGL build +#endif #endif diff --git a/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt b/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt index c0221efd1..6edf2072c 100644 --- a/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt @@ -40,22 +40,37 @@ if(NOT BUILD_PLUGINS_STATIC) # First replace ${} variables, then $<> generator expressions configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) - file(GENERATE OUTPUT $/configure.h + file(GENERATE OUTPUT $/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) else() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) endif() -corrade_add_test(MagnumFontGLTest MagnumFontGLTest.cpp - LIBRARIES MagnumText MagnumTrade MagnumOpenGLTester +corrade_add_test(MagnumFontTest MagnumFontTest.cpp + LIBRARIES MagnumText MagnumTrade FILES font.conf font.tga) if(NOT BUILD_PLUGINS_STATIC) - target_include_directories(MagnumFontGLTest PRIVATE $) + target_include_directories(MagnumFontTest PRIVATE $) else() - target_include_directories(MagnumFontGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - target_link_libraries(MagnumFontGLTest PRIVATE MagnumFont TgaImporter) + target_include_directories(MagnumFontTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(MagnumFontTest PRIVATE MagnumFont TgaImporter) +endif() +set_target_properties(MagnumFontTest PROPERTIES FOLDER "MagnumPlugins/MagnumFont/Test") + +if(BUILD_GL_TESTS) + corrade_add_test(MagnumFontGLTest MagnumFontGLTest.cpp + LIBRARIES MagnumText MagnumTrade MagnumOpenGLTester + FILES + font.conf + font.tga) + if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(MagnumFontGLTest PRIVATE $) + else() + target_include_directories(MagnumFontGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(MagnumFontGLTest PRIVATE MagnumFont TgaImporter) + endif() + set_target_properties(MagnumFontGLTest PROPERTIES FOLDER "MagnumPlugins/MagnumFont/Test") endif() -set_target_properties(MagnumFontGLTest PROPERTIES FOLDER "MagnumPlugins/MagnumFont/Test") diff --git a/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp b/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp index 3cdf55985..ada22e581 100644 --- a/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp +++ b/src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp @@ -38,9 +38,6 @@ namespace Magnum { namespace Text { namespace Test { namespace { struct MagnumFontGLTest: GL::OpenGLTester { explicit MagnumFontGLTest(); - void nonexistent(); - void properties(); - void layout(); void createGlyphCache(); /* Explicitly forbid system-wide plugin dependencies */ @@ -49,10 +46,7 @@ struct MagnumFontGLTest: GL::OpenGLTester { }; MagnumFontGLTest::MagnumFontGLTest() { - addTests({&MagnumFontGLTest::nonexistent, - &MagnumFontGLTest::properties, - &MagnumFontGLTest::layout, - &MagnumFontGLTest::createGlyphCache}); + addTests({&MagnumFontGLTest::createGlyphCache}); /* Load the plugins directly from the build tree. Otherwise they're static and already loaded. */ @@ -62,77 +56,13 @@ MagnumFontGLTest::MagnumFontGLTest() { #endif } -void MagnumFontGLTest::nonexistent() { - Containers::Pointer font = _fontManager.instantiate("MagnumFont"); - - std::ostringstream out; - Error redirectError{&out}; - CORRADE_VERIFY(!font->openFile("nonexistent.conf", 0.0f)); - CORRADE_COMPARE(out.str(), "Text::MagnumFont::openFile(): cannot open file nonexistent.conf\n"); -} - -void MagnumFontGLTest::properties() { - Containers::Pointer font = _fontManager.instantiate("MagnumFont"); - - CORRADE_VERIFY(font->openFile(Utility::Directory::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); - CORRADE_COMPARE(font->size(), 16.0f); - CORRADE_COMPARE(font->ascent(), 25.0f); - CORRADE_COMPARE(font->descent(), -10.0f); - CORRADE_COMPARE(font->lineHeight(), 39.7333f); - CORRADE_COMPARE(font->glyphAdvance(font->glyphId(U'W')), Vector2(23.0f, 0.0f)); -} - -void MagnumFontGLTest::layout() { - Containers::Pointer font = _fontManager.instantiate("MagnumFont"); - - CORRADE_VERIFY(font->openFile(Utility::Directory::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); - - /* Fill the cache with some fake glyphs */ - GlyphCache 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"); - CORRADE_VERIFY(layouter); - CORRADE_COMPARE(layouter->glyphCount(), 4); - - Range2D rectangle; - Range2D position; - Range2D textureCoordinates; - - /* 'W' */ - Vector2 cursorPosition; - std::tie(position, textureCoordinates) = layouter->renderGlyph(0, cursorPosition = {}, rectangle); - CORRADE_COMPARE(position, Range2D({0.78125f, 1.0625f}, {1.28125f, 4.8125f})); - CORRADE_COMPARE(textureCoordinates, Range2D({0, 0.03125f}, {0.0625f, 0.5f})); - CORRADE_COMPARE(cursorPosition, Vector2(0.71875f, 0.0f)); - - /* 'a' (not found) */ - std::tie(position, textureCoordinates) = layouter->renderGlyph(1, cursorPosition = {}, rectangle); - CORRADE_COMPARE(position, Range2D()); - CORRADE_COMPARE(textureCoordinates, Range2D()); - CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); - - /* 'v' (not found) */ - std::tie(position, textureCoordinates) = layouter->renderGlyph(2, cursorPosition = {}, rectangle); - CORRADE_COMPARE(position, Range2D()); - CORRADE_COMPARE(textureCoordinates, Range2D()); - CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); - - /* 'e' */ - std::tie(position, textureCoordinates) = layouter->renderGlyph(3, cursorPosition = {}, rectangle); - CORRADE_COMPARE(position, Range2D({0.78125f, 0.375f}, {2.28125f, 1.25f})); - CORRADE_COMPARE(textureCoordinates, Range2D({0.0625f, 0.015625f}, {0.25f, 0.125f})); - CORRADE_COMPARE(cursorPosition, Vector2(0.375f, 0.0f)); -} - void MagnumFontGLTest::createGlyphCache() { Containers::Pointer font = _fontManager.instantiate("MagnumFont"); CORRADE_VERIFY(font->openFile(Utility::Directory::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); /* Just testing that nothing crashes, asserts or errors */ - Containers::Pointer cache = font->createGlyphCache(); + Containers::Pointer cache = font->createGlyphCache(); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_VERIFY(cache); diff --git a/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp b/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp new file mode 100644 index 000000000..81f9d191a --- /dev/null +++ b/src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp @@ -0,0 +1,135 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/GL/OpenGLTester.h" +#include "Magnum/Text/AbstractFont.h" +#include "Magnum/Text/AbstractGlyphCache.h" +#include "Magnum/Trade/AbstractImporter.h" + +#include "configure.h" + +namespace Magnum { namespace Text { namespace Test { namespace { + +struct MagnumFontTest: TestSuite::Tester { + explicit MagnumFontTest(); + + void nonexistent(); + void properties(); + void layout(); + + /* Explicitly forbid system-wide plugin dependencies */ + PluginManager::Manager _importerManager{"nonexistent"}; + PluginManager::Manager _fontManager{"nonexistent"}; +}; + +MagnumFontTest::MagnumFontTest() { + addTests({&MagnumFontTest::nonexistent, + &MagnumFontTest::properties, + &MagnumFontTest::layout}); + + /* Load the plugins directly from the build tree. Otherwise they're static + and already loaded. */ + #if defined(TGAIMPORTER_PLUGIN_FILENAME) && defined(MAGNUMFONT_PLUGIN_FILENAME) + CORRADE_INTERNAL_ASSERT(_importerManager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + CORRADE_INTERNAL_ASSERT(_fontManager.load(MAGNUMFONT_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void MagnumFontTest::nonexistent() { + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!font->openFile("nonexistent.conf", 0.0f)); + CORRADE_COMPARE(out.str(), "Text::MagnumFont::openFile(): cannot open file nonexistent.conf\n"); +} + +void MagnumFontTest::properties() { + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); + + CORRADE_VERIFY(font->openFile(Utility::Directory::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); + CORRADE_COMPARE(font->size(), 16.0f); + CORRADE_COMPARE(font->ascent(), 25.0f); + CORRADE_COMPARE(font->descent(), -10.0f); + CORRADE_COMPARE(font->lineHeight(), 39.7333f); + CORRADE_COMPARE(font->glyphAdvance(font->glyphId(U'W')), Vector2(23.0f, 0.0f)); +} + +void MagnumFontTest::layout() { + Containers::Pointer font = _fontManager.instantiate("MagnumFont"); + + CORRADE_VERIFY(font->openFile(Utility::Directory::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f)); + + /* Fill the cache with some fake glyphs */ + struct DummyGlyphCache: AbstractGlyphCache { + using AbstractGlyphCache::AbstractGlyphCache; + + GlyphCacheFeatures doFeatures() const override { return {}; } + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } 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"); + CORRADE_VERIFY(layouter); + CORRADE_COMPARE(layouter->glyphCount(), 4); + + Range2D rectangle; + Range2D position; + Range2D textureCoordinates; + + /* 'W' */ + Vector2 cursorPosition; + std::tie(position, textureCoordinates) = layouter->renderGlyph(0, cursorPosition = {}, rectangle); + CORRADE_COMPARE(position, Range2D({0.78125f, 1.0625f}, {1.28125f, 4.8125f})); + CORRADE_COMPARE(textureCoordinates, Range2D({0, 0.03125f}, {0.0625f, 0.5f})); + CORRADE_COMPARE(cursorPosition, Vector2(0.71875f, 0.0f)); + + /* 'a' (not found) */ + std::tie(position, textureCoordinates) = layouter->renderGlyph(1, cursorPosition = {}, rectangle); + CORRADE_COMPARE(position, Range2D()); + CORRADE_COMPARE(textureCoordinates, Range2D()); + CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); + + /* 'v' (not found) */ + std::tie(position, textureCoordinates) = layouter->renderGlyph(2, cursorPosition = {}, rectangle); + CORRADE_COMPARE(position, Range2D()); + CORRADE_COMPARE(textureCoordinates, Range2D()); + CORRADE_COMPARE(cursorPosition, Vector2(0.25f, 0.0f)); + + /* 'e' */ + std::tie(position, textureCoordinates) = layouter->renderGlyph(3, cursorPosition = {}, rectangle); + CORRADE_COMPARE(position, Range2D({0.78125f, 0.375f}, {2.28125f, 1.25f})); + CORRADE_COMPARE(textureCoordinates, Range2D({0.0625f, 0.015625f}, {0.25f, 0.125f})); + CORRADE_COMPARE(cursorPosition, Vector2(0.375f, 0.0f)); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::MagnumFontTest) diff --git a/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt b/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt index f74d949f2..814474406 100644 --- a/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt @@ -56,7 +56,7 @@ if(BUILD_PLUGINS_STATIC) target_sources(MagnumFontConverter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) endif() -if(BUILD_GL_TESTS) +if(BUILD_TESTS) add_subdirectory(Test) endif() diff --git a/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp b/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp index f1e392cb9..ad4b44951 100644 --- a/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp +++ b/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp @@ -32,8 +32,8 @@ #include "Magnum/Image.h" #include "Magnum/PixelFormat.h" -#include "Magnum/Text/GlyphCache.h" #include "Magnum/Text/AbstractFont.h" +#include "Magnum/Text/AbstractGlyphCache.h" #include "MagnumPlugins/TgaImageConverter/TgaImageConverter.h" namespace Magnum { namespace Text { @@ -46,7 +46,7 @@ auto MagnumFontConverter::doFeatures() const -> Features { return Feature::ExportFont|Feature::ConvertData|Feature::MultiFile; } -std::vector>> MagnumFontConverter::doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const { +std::vector>> MagnumFontConverter::doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const { Utility::Configuration configuration; configuration.setValue("version", 1); @@ -102,9 +102,7 @@ std::vector>> MagnumFontConverter std::copy(confStr.begin(), confStr.end(), confData.begin()); /* Save cache image */ - Image2D image{PixelFormat::R8Unorm}; - cache.texture().image(0, image); - auto tgaData = Trade::TgaImageConverter().exportToData(image); + auto tgaData = Trade::TgaImageConverter().exportToData(cache.image()); std::vector>> out; out.emplace_back(filename + ".conf", std::move(confData)); @@ -115,4 +113,4 @@ std::vector>> MagnumFontConverter }} CORRADE_PLUGIN_REGISTER(MagnumFontConverter, Magnum::Text::MagnumFontConverter, - "cz.mosra.magnum.Text.AbstractFontConverter/0.1.2") + "cz.mosra.magnum.Text.AbstractFontConverter/0.2") diff --git a/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h b/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h index 0c9a022fd..1b15d8bb6 100644 --- a/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h +++ b/src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h @@ -55,10 +55,10 @@ namespace Magnum { namespace Text { @brief MagnumFont converter plugin Expects filename prefix, creates two files, `prefix.conf` and `prefix.tga`. See -@ref MagnumFont for more information about the font. +@ref MagnumFont for more information about the font. The plugin requires the +passed @ref AbstractGlyphCache to support @ref GlyphCacheFeature::ImageDownload. -This plugin is available only on desktop OpenGL, as it uses @ref GL::Texture::image() -to read back the generated data. It depends on the @ref Text library and the +This plugin depends on the @ref Text library and the @ref Trade::TgaImageConverter "TgaImageConverter" plugin. It is built if `WITH_MAGNUMFONTCONVERTER` is enabled when building Magnum. To use as a dynamic plugin, you need to load the @cpp "MagnumFontConverter" @ce plugin from @@ -78,7 +78,7 @@ class MAGNUM_MAGNUMFONTCONVERTER_EXPORT MagnumFontConverter: public Text::Abstra private: MAGNUM_MAGNUMFONTCONVERTER_LOCAL Features doFeatures() const override; - MAGNUM_MAGNUMFONTCONVERTER_LOCAL std::vector>> doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const override; + MAGNUM_MAGNUMFONTCONVERTER_LOCAL std::vector>> doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const override; }; }} diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt b/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt index 941a24b8e..e9a9e04be 100644 --- a/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt @@ -37,23 +37,23 @@ if(NOT BUILD_PLUGINS_STATIC) # First replace ${} variables, then $<> generator expressions configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) - file(GENERATE OUTPUT $/configure.h + file(GENERATE OUTPUT $/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) else() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) endif() -corrade_add_test(MagnumFontConverterGLTest MagnumFontConverterGLTest.cpp - LIBRARIES MagnumText MagnumTrade MagnumOpenGLTester) +corrade_add_test(MagnumFontConverterTest MagnumFontConverterTest.cpp + LIBRARIES MagnumText MagnumTrade) if(NOT BUILD_PLUGINS_STATIC) - target_include_directories(MagnumFontConverterGLTest PRIVATE $) + target_include_directories(MagnumFontConverterTest PRIVATE $) else() - target_include_directories(MagnumFontConverterGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - target_link_libraries(MagnumFontConverterGLTest PRIVATE + target_include_directories(MagnumFontConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(MagnumFontConverterTest PRIVATE MagnumFontConverter) # TgaImageConverter should get linked transitively if(WITH_TGAIMPORTER) - target_link_libraries(MagnumFontConverterGLTest PRIVATE TgaImporter) + target_link_libraries(MagnumFontConverterTest PRIVATE TgaImporter) endif() endif() -set_target_properties(MagnumFontConverterGLTest PROPERTIES FOLDER "MagnumPlugins/MagnumFontConverter/Test") +set_target_properties(MagnumFontConverterTest PROPERTIES FOLDER "MagnumPlugins/MagnumFontConverter/Test") diff --git a/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterGLTest.cpp b/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp similarity index 84% rename from src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterGLTest.cpp rename to src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp index 892da3adf..525da2208 100644 --- a/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterGLTest.cpp +++ b/src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp @@ -23,15 +23,15 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include +#include #include +#include "Magnum/Image.h" #include "Magnum/PixelFormat.h" -#include "Magnum/GL/Context.h" -#include "Magnum/GL/Extensions.h" -#include "Magnum/GL/TextureFormat.h" -#include "Magnum/GL/OpenGLTester.h" -#include "Magnum/Text/GlyphCache.h" +#include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFontConverter.h" #include "Magnum/Trade/AbstractImageConverter.h" @@ -42,8 +42,8 @@ namespace Magnum { namespace Text { namespace Test { namespace { -struct MagnumFontConverterGLTest: GL::OpenGLTester { - explicit MagnumFontConverterGLTest(); +struct MagnumFontConverterTest: TestSuite::Tester { + explicit MagnumFontConverterTest(); void exportFont(); @@ -53,8 +53,8 @@ struct MagnumFontConverterGLTest: GL::OpenGLTester { PluginManager::Manager _importerManager{"nonexistent"}; }; -MagnumFontConverterGLTest::MagnumFontConverterGLTest() { - addTests({&MagnumFontConverterGLTest::exportFont}); +MagnumFontConverterTest::MagnumFontConverterTest() { + addTests({&MagnumFontConverterTest::exportFont}); /* Load the plugins directly from the build tree. Otherwise they are static and already loaded. */ @@ -68,7 +68,7 @@ MagnumFontConverterGLTest::MagnumFontConverterGLTest() { #endif } -void MagnumFontConverterGLTest::exportFont() { +void MagnumFontConverterTest::exportFont() { /* Remove previously created files */ Utility::Directory::rm(Utility::Directory::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.conf")); Utility::Directory::rm(Utility::Directory::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.tga")); @@ -86,7 +86,7 @@ void MagnumFontConverterGLTest::exportFont() { return {16.0f, 25.0f, -10.0f, 39.7333f}; } Features doFeatures() const { return {}; } - Containers::Pointer doLayout(const GlyphCache&, Float, const std::string&) { return nullptr; } + Containers::Pointer doLayout(const AbstractGlyphCache&, Float, const std::string&) { return nullptr; } UnsignedInt doGlyphId(const char32_t character) { switch(character) { @@ -112,8 +112,15 @@ void MagnumFontConverterGLTest::exportFont() { font.openFile({}, {}); /* Create fake cache */ - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg); - GlyphCache cache(GL::TextureFormat::R8, Vector2i(1536), Vector2i(256), Vector2i(24)); + struct MyCache: AbstractGlyphCache { + explicit MyCache(): AbstractGlyphCache{Vector2i{1536}, Vector2i{24}} {} + + 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{Containers::ValueInit, 256*256}}; + } + } 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}}); @@ -130,7 +137,8 @@ void MagnumFontConverterGLTest::exportFont() { 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 */ + /* 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(Utility::Directory::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.tga"))); Containers::Optional image = importer->image2D(0); @@ -141,4 +149,4 @@ void MagnumFontConverterGLTest::exportFont() { }}}} -CORRADE_TEST_MAIN(Magnum::Text::Test::MagnumFontConverterGLTest) +CORRADE_TEST_MAIN(Magnum::Text::Test::MagnumFontConverterTest)