Browse Source

Text: API-independent base for glyph caches.

Allows the Font and FontConverter plugins be built without TARGET_GL
enabled. That was the last piece missing for making the magnum-plugins
repo completely GL-free.
pull/325/head
Vladimír Vondruš 7 years ago
parent
commit
834c5fe40d
  1. 4
      CMakeLists.txt
  2. 4
      doc/building.dox
  3. 9
      doc/changelog.dox
  4. 2
      doc/custom-buildsystems-order.dot
  5. 9
      modules/FindMagnum.cmake
  6. 18
      src/Magnum/Text/AbstractFont.cpp
  7. 14
      src/Magnum/Text/AbstractFont.h
  8. 40
      src/Magnum/Text/AbstractFontConverter.cpp
  9. 38
      src/Magnum/Text/AbstractFontConverter.h
  10. 76
      src/Magnum/Text/AbstractGlyphCache.cpp
  11. 203
      src/Magnum/Text/AbstractGlyphCache.h
  12. 81
      src/Magnum/Text/CMakeLists.txt
  13. 2
      src/Magnum/Text/DistanceFieldGlyphCache.cpp
  14. 20
      src/Magnum/Text/DistanceFieldGlyphCache.h
  15. 65
      src/Magnum/Text/GlyphCache.cpp
  16. 115
      src/Magnum/Text/GlyphCache.h
  17. 1
      src/Magnum/Text/Renderer.cpp
  18. 6
      src/Magnum/Text/Renderer.h
  19. 75
      src/Magnum/Text/Test/AbstractFontConverterTest.cpp
  20. 418
      src/Magnum/Text/Test/AbstractFontTest.cpp
  21. 192
      src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp
  22. 7
      src/Magnum/Text/Test/CMakeLists.txt
  23. 44
      src/Magnum/Text/Test/GlyphCacheGLTest.cpp
  24. 4
      src/Magnum/Text/Test/RendererGLTest.cpp
  25. 8
      src/Magnum/Text/Text.h
  26. 2
      src/Magnum/Text/visibility.h
  27. 2
      src/MagnumPlugins/MagnumFont/CMakeLists.txt
  28. 14
      src/MagnumPlugins/MagnumFont/MagnumFont.cpp
  29. 10
      src/MagnumPlugins/MagnumFont/MagnumFont.h
  30. 29
      src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt
  31. 74
      src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp
  32. 135
      src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp
  33. 2
      src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt
  34. 10
      src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp
  35. 8
      src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h
  36. 16
      src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt
  37. 38
      src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp

4
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)

4
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.

9
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

2
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]

9
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()

18
src/Magnum/Text/AbstractFont.cpp

@ -30,7 +30,7 @@
#include <Corrade/Utility/Unicode.h>
#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<GlyphCache> AbstractFont::createGlyphCache() {
Containers::Pointer<AbstractGlyphCache> AbstractFont::createGlyphCache() {
CORRADE_ASSERT(isOpened(),
"Text::AbstractFont::createGlyphCache(): no font opened", nullptr);
CORRADE_ASSERT(features() & Feature::PreparedGlyphCache,
@ -175,12 +175,12 @@ Containers::Pointer<GlyphCache> AbstractFont::createGlyphCache() {
return doCreateGlyphCache();
}
Containers::Pointer<GlyphCache> AbstractFont::doCreateGlyphCache() {
Containers::Pointer<AbstractGlyphCache> AbstractFont::doCreateGlyphCache() {
CORRADE_ASSERT(false, "Text::AbstractFont::createGlyphCache(): feature advertised but not implemented", nullptr);
return nullptr;
}
Containers::Pointer<AbstractLayouter> AbstractFont::layout(const GlyphCache& cache, const Float size, const std::string& text) {
Containers::Pointer<AbstractLayouter> 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);

14
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<GlyphCache> createGlyphCache();
Containers::Pointer<AbstractGlyphCache> 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<AbstractLayouter> layout(const GlyphCache& cache, Float size, const std::string& text);
Containers::Pointer<AbstractLayouter> 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<GlyphCache> doCreateGlyphCache();
virtual Containers::Pointer<AbstractGlyphCache> doCreateGlyphCache();
/** @brief Implementation for @ref layout() */
virtual Containers::Pointer<AbstractLayouter> doLayout(const GlyphCache& cache, Float size, const std::string& text) = 0;
virtual Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache& cache, Float size, const std::string& text) = 0;
#ifdef DOXYGEN_GENERATING_OUTPUT
private:

40
src/Magnum/Text/AbstractFontConverter.cpp

@ -31,7 +31,7 @@
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/Unicode.h>
#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<std::pair<std::string, Containers::Array<char>>> AbstractFontConverter::exportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> AbstractFontConverter::doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> AbstractFontConvert
return out;
}
Containers::Array<char> AbstractFontConverter::exportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::string& characters) const {
Containers::Array<char> 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<char> AbstractFontConverter::exportFontToSingleData(AbstractFo
return doExportFontToSingleData(font, cache, uniqueUnicode(characters));
}
Containers::Array<char> AbstractFontConverter::doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string&) const {
Containers::Array<char> 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<std::pair<std::string, Containers::Array<char>>> AbstractFontConverter::exportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> AbstractFontConverter::doExportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> AbstractFontConvert
return out;
}
Containers::Array<char> AbstractFontConverter::exportGlyphCacheToSingleData(GlyphCache& cache) const {
Containers::Array<char> 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<char> AbstractFontConverter::exportGlyphCacheToSingleData(Glyp
return doExportGlyphCacheToSingleData(cache);
}
Containers::Array<char> AbstractFontConverter::doExportGlyphCacheToSingleData(GlyphCache&) const {
Containers::Array<char> 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<GlyphCache> AbstractFontConverter::importGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& data) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::importGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& 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<GlyphCache> AbstractFontConverter::importGlyphCacheFromData(
return doImportGlyphCacheFromData(data);
}
Containers::Pointer<GlyphCache> AbstractFontConverter::doImportGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& data) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::doImportGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& 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<GlyphCache> AbstractFontConverter::doImportGlyphCacheFromDat
return doImportGlyphCacheFromSingleData(data[0].second);
}
Containers::Pointer<GlyphCache> AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayView<const char> data) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayView<const char> 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<GlyphCache> AbstractFontConverter::importGlyphCacheFromSingl
return doImportGlyphCacheFromSingleData(data);
}
Containers::Pointer<GlyphCache> AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayView<const char>) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayView<const char>) const {
CORRADE_ASSERT(false,
"Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature advertised but not implemented", nullptr);
return nullptr;
}
Containers::Pointer<GlyphCache> AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const {
CORRADE_ASSERT(features() & Feature::ImportGlyphCache,
"Text::AbstractFontConverter::importGlyphCacheFromFile(): feature not supported", nullptr);
return doImportGlyphCacheFromFile(filename);
}
Containers::Pointer<GlyphCache> AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const {
Containers::Pointer<AbstractGlyphCache> AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const {
CORRADE_ASSERT(features() & Feature::ConvertData && !(features() & Feature::MultiFile),
"Text::AbstractFontConverter::importGlyphCacheFromFile(): not implemented", nullptr);

38
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<std::pair<std::string, Containers::Array<char>>> exportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::string& characters) const;
std::vector<std::pair<std::string, Containers::Array<char>>> 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<char> exportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::string& characters) const;
Containers::Array<char> 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<std::pair<std::string, Containers::Array<char>>> exportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const;
std::vector<std::pair<std::string, Containers::Array<char>>> 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<char> exportGlyphCacheToSingleData(GlyphCache& cache) const;
Containers::Array<char> 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<GlyphCache> importGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& data) const;
Containers::Pointer<AbstractGlyphCache> importGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& 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<GlyphCache> importGlyphCacheFromSingleData(Containers::ArrayView<const char> data) const;
Containers::Pointer<AbstractGlyphCache> importGlyphCacheFromSingleData(Containers::ArrayView<const char> 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<GlyphCache> importGlyphCacheFromFile(const std::string& filename) const;
Containers::Pointer<AbstractGlyphCache> 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<std::pair<std::string, Containers::Array<char>>> doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const;
virtual std::vector<std::pair<std::string, Containers::Array<char>>> doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const;
/** @brief Implementation for @ref exportFontToSingleData() */
virtual Containers::Array<char> doExportFontToSingleData(AbstractFont& font, GlyphCache& cache, const std::u32string& characters) const;
virtual Containers::Array<char> 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<std::pair<std::string, Containers::Array<char>>> doExportGlyphCacheToData(GlyphCache& cache, const std::string& filename) const;
virtual std::vector<std::pair<std::string, Containers::Array<char>>> doExportGlyphCacheToData(AbstractGlyphCache& cache, const std::string& filename) const;
/** @brief Implementation for @ref exportGlyphCacheToSingleData() */
virtual Containers::Array<char> doExportGlyphCacheToSingleData(GlyphCache& cache) const;
virtual Containers::Array<char> 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<GlyphCache> doImportGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& data) const;
virtual Containers::Pointer<AbstractGlyphCache> doImportGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayView<const char>>>& data) const;
/** @brief Implementation for @ref importGlyphCacheFromSingleData() */
virtual Containers::Pointer<GlyphCache> doImportGlyphCacheFromSingleData(Containers::ArrayView<const char> data) const;
virtual Containers::Pointer<AbstractGlyphCache> doImportGlyphCacheFromSingleData(Containers::ArrayView<const char> 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<GlyphCache> doImportGlyphCacheFromFile(const std::string& filename) const;
virtual Containers::Pointer<AbstractGlyphCache> doImportGlyphCacheFromFile(const std::string& filename) const;
};
CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features)

76
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š <mosra@centrum.cz>
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<Vector2i, Range2Di>{}});
}
AbstractGlyphCache::~AbstractGlyphCache() = default;
std::vector<Range2Di> AbstractGlyphCache::reserve(const std::vector<Vector2i>& sizes) {
CORRADE_ASSERT((glyphs.size() == 1 && glyphs.at(0) == std::pair<Vector2i, Range2Di>()),
"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<Vector2i, Range2Di> 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{{}});
}
}}

203
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š <mosra@centrum.cz>
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 <vector>
#include <unordered_map>
#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<GlyphCacheFeature> 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<Vector2i, Range2Di> 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<UnsignedInt, std::pair<Vector2i, Range2Di>>::const_iterator begin() const {
return glyphs.begin();
}
/** @brief Iterator access to cache data */
std::unordered_map<UnsignedInt, std::pair<Vector2i, Range2Di>>::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<Range2Di> reserve(const std::vector<Vector2i>& 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<UnsignedInt, std::pair<Vector2i, Range2Di>> glyphs;
};
}}
#endif

81
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
$<TARGET_PROPERTY:Corrade::PluginManager,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:Magnum,INTERFACE_INCLUDE_DIRECTORIES>)
if(TARGET_GL)
target_include_directories(MagnumTextObjects PUBLIC $<TARGET_PROPERTY:MagnumGL,INTERFACE_INCLUDE_DIRECTORIES>)
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}
$<TARGET_OBJECTS:MagnumTextObjects>
${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}
$<TARGET_OBJECTS:MagnumTextObjects>
${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()

2
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)

20
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

65
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<Vector2i, Range2Di>{}});
}
std::vector<Range2Di> GlyphCache::reserve(const std::vector<Vector2i>& sizes) {
CORRADE_ASSERT((glyphs.size() == 1 && glyphs.at(0) == std::pair<Vector2i, Range2Di>()),
"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<Vector2i, Range2Di> 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
}}

115
src/Magnum/Text/GlyphCache.h

@ -29,12 +29,11 @@
* @brief Class @ref Magnum::Text::GlyphCache
*/
#include <vector>
#include <unordered_map>
#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<Vector2i, Range2Di> 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<UnsignedInt, std::pair<Vector2i, Range2Di>>::const_iterator begin() const {
return glyphs.begin();
}
/** @brief Iterator access to cache data */
std::unordered_map<UnsignedInt, std::pair<Vector2i, Range2Di>>::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<Range2Di> reserve(const std::vector<Vector2i>& 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<UnsignedInt, std::pair<Vector2i, Range2Di>> glyphs;
};
}}
#else
#error this header is available only in the OpenGL build
#endif
#endif

1
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 {

6
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 <string>
#include <tuple>
#include <vector>
@ -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

75
src/Magnum/Text/Test/AbstractFontConverterTest.cpp

@ -29,8 +29,9 @@
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/Directory.h>
#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<GlyphCache*>(nullptr) makes Clang Analyzer grumpy */
char nullData;
AbstractFont& nullFont = *reinterpret_cast<AbstractFont*>(nullData);
GlyphCache& nullGlyphCache = *reinterpret_cast<GlyphCache*>(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<AbstractLayouter> 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<char> doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string& characters) const override {
Containers::Array<char> 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<char> doExportFontToSingleData(AbstractFont&, GlyphCache&, const std::u32string&) const override {
Containers::Array<char> doExportFontToSingleData(AbstractFont&, AbstractGlyphCache&, const std::u32string&) const override {
Containers::Array<char> 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<std::pair<std::string, Containers::Array<char>>> doExportFontToData(AbstractFont&, GlyphCache&, const std::string& filename, const std::u32string&) const override {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> 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<char> doExportGlyphCacheToSingleData(GlyphCache&) const override {
return Containers::Array<char>{Containers::InPlaceInit, {'\xee'}};
}
};
Containers::Array<char> doExportGlyphCacheToSingleData(AbstractGlyphCache&) const override {
return Containers::Array<char>{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<std::pair<std::string, Containers::Array<char>>> doExportGlyphCacheToData(GlyphCache&, const std::string& filename) const override {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> 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<GlyphCache> doImportGlyphCacheFromSingleData(const Containers::ArrayView<const char> data) const override {
Containers::Pointer<AbstractGlyphCache> doImportGlyphCacheFromSingleData(const Containers::ArrayView<const char> data) const override {
if(data.size() == 1 && data[0] == '\xa5')
return Containers::Pointer<GlyphCache>(reinterpret_cast<GlyphCache*>(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<GlyphCache> cache = importer.importGlyphCacheFromData({{{}, data}});
CORRADE_COMPARE(cache.get(), reinterpret_cast<GlyphCache*>(0xdeadbeef));
/* The pointer is invalid, avoid deletion */
cache.release();
Containers::Pointer<AbstractGlyphCache> cache = importer.importGlyphCacheFromData({{{}, data}});
CORRADE_COMPARE(cache->textureSize(), (Vector2i{123, 345}));
}
void AbstractFontConverterTest::importGlyphCacheFromFile() {
/* doImportFromFile() should call doImportFromSingleData() */
SingleGlyphCacheDataImporter importer;
Containers::Pointer<GlyphCache> cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin"));
CORRADE_COMPARE(cache.get(), reinterpret_cast<GlyphCache*>(0xdeadbeef));
/* The pointer is invalid, avoid deletion */
cache.release();
Containers::Pointer<AbstractGlyphCache> cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin"));
CORRADE_COMPARE(cache->textureSize(), (Vector2i{123, 345}));
}
}}}}

418
src/Magnum/Text/Test/AbstractFontTest.cpp

@ -23,12 +23,14 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/Directory.h>
#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<AbstractLayouter> doLayout(const GlyphCache&, Float, const std::string&) override {
Containers::Pointer<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<Range2D, Range2D, Vector2> 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<AbstractLayouter> doLayout(const AbstractGlyphCache& cache, Float size, const std::string& str) override {
return Containers::pointer<Layouter>(UnsignedInt(cache.textureSize().x()*str.size()*size));
}
} font;
DummyGlyphCache cache{{100, 200}};
Containers::Pointer<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<Vector2i, Range2Di>{{52, 208}, {}}));
CORRADE_COMPARE(cache['e'*10], (std::pair<Vector2i, Range2Di>{{50, 202}, {}}));
CORRADE_COMPARE(cache['l'*10], (std::pair<Vector2i, Range2Di>{{54, 216}, {}}));
CORRADE_COMPARE(cache['o'*10], (std::pair<Vector2i, Range2Di>{{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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> doLayout(const AbstractGlyphCache&, Float, const std::string&) override { return nullptr; }
Containers::Pointer<AbstractGlyphCache> doCreateGlyphCache() override {
Containers::Pointer<AbstractGlyphCache> cache{new DummyGlyphCache{{100, 100}}};
for(char a: std::string{"helo"}) cache->insert(a*10, {a/2, a*2}, {});
return cache;
}
} font;
Containers::Pointer<AbstractGlyphCache> cache = font.createGlyphCache();
CORRADE_COMPARE(cache->glyphCount(), 5);
CORRADE_COMPARE((*cache)['h'*10], (std::pair<Vector2i, Range2Di>{{52, 208}, {}}));
CORRADE_COMPARE((*cache)['e'*10], (std::pair<Vector2i, Range2Di>{{50, 202}, {}}));
CORRADE_COMPARE((*cache)['l'*10], (std::pair<Vector2i, Range2Di>{{54, 216}, {}}));
CORRADE_COMPARE((*cache)['o'*10], (std::pair<Vector2i, Range2Di>{{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<AbstractLayouter> 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<AbstractLayouter> 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<AbstractLayouter> 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)

192
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š <mosra@centrum.cz>
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 <sstream>
#include <tuple>
#include <Corrade/TestSuite/Tester.h>
#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)

7
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")

44
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)

4
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<AbstractLayouter> doLayout(const GlyphCache&, const Float size, const std::string& text) override {
Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache&, const Float size, const std::string& text) override {
return Containers::Pointer<AbstractLayouter>(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<AbstractLayouter> doLayout(const GlyphCache&, Float, const std::string& text) override {
Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache&, Float, const std::string& text) override {
return Containers::Pointer<AbstractLayouter>(new Layouter(text.size()));
}

8
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<UnsignedInt> class Renderer;
typedef Renderer<2> Renderer2D;
typedef Renderer<3> Renderer3D;
#endif
#endif
}}

2
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

2
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()

14
src/MagnumPlugins/MagnumFont/MagnumFont.cpp

@ -48,13 +48,13 @@ struct MagnumFont::Data {
namespace {
class MagnumFontLayouter: public AbstractLayouter {
public:
explicit MagnumFontLayouter(const std::vector<Vector2>& glyphAdvance, const GlyphCache& cache, Float fontSize, Float textSize, std::vector<UnsignedInt>&& glyphs);
explicit MagnumFontLayouter(const std::vector<Vector2>& glyphAdvance, const AbstractGlyphCache& cache, Float fontSize, Float textSize, std::vector<UnsignedInt>&& glyphs);
private:
std::tuple<Range2D, Range2D, Vector2> doRenderGlyph(UnsignedInt i) override;
const std::vector<Vector2>& glyphAdvance;
const GlyphCache& cache;
const AbstractGlyphCache& cache;
const Float fontSize, textSize;
const std::vector<UnsignedInt> glyphs;
};
@ -184,9 +184,9 @@ Vector2 MagnumFont::doGlyphAdvance(const UnsignedInt glyph) {
return glyph < _opened->glyphAdvance.size() ? _opened->glyphAdvance[glyph] : Vector2();
}
Containers::Pointer<GlyphCache> MagnumFont::doCreateGlyphCache() {
Containers::Pointer<AbstractGlyphCache> MagnumFont::doCreateGlyphCache() {
/* Set cache image */
Containers::Pointer<GlyphCache> cache(new Text::GlyphCache(
Containers::Pointer<AbstractGlyphCache> cache(new Text::GlyphCache(
_opened->conf.value<Vector2i>("originalImageSize"),
_opened->image.size(),
_opened->conf.value<Vector2i>("padding")));
@ -200,7 +200,7 @@ Containers::Pointer<GlyphCache> MagnumFont::doCreateGlyphCache() {
return cache;
}
Containers::Pointer<AbstractLayouter> MagnumFont::doLayout(const GlyphCache& cache, Float size, const std::string& text) {
Containers::Pointer<AbstractLayouter> MagnumFont::doLayout(const AbstractGlyphCache& cache, Float size, const std::string& text) {
/* Get glyph codes from characters */
std::vector<UnsignedInt> glyphs;
glyphs.reserve(text.size());
@ -216,7 +216,7 @@ Containers::Pointer<AbstractLayouter> MagnumFont::doLayout(const GlyphCache& cac
namespace {
MagnumFontLayouter::MagnumFontLayouter(const std::vector<Vector2>& glyphAdvance, const GlyphCache& cache, const Float fontSize, const Float textSize, std::vector<UnsignedInt>&& glyphs): AbstractLayouter(glyphs.size()), glyphAdvance(glyphAdvance), cache(cache), fontSize(fontSize), textSize(textSize), glyphs(std::move(glyphs)) {}
MagnumFontLayouter::MagnumFontLayouter(const std::vector<Vector2>& glyphAdvance, const AbstractGlyphCache& cache, const Float fontSize, const Float textSize, std::vector<UnsignedInt>&& glyphs): AbstractLayouter(glyphs.size()), glyphAdvance(glyphAdvance), cache(cache), fontSize(fontSize), textSize(textSize), glyphs(std::move(glyphs)) {}
std::tuple<Range2D, Range2D, Vector2> MagnumFontLayouter::doRenderGlyph(const UnsignedInt i) {
/* Position of the texture in the resulting glyph, texture coordinates */
@ -242,4 +242,4 @@ std::tuple<Range2D, Range2D, Vector2> 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")

10
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<GlyphCache> doCreateGlyphCache() override;
MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer<AbstractLayouter> doLayout(const GlyphCache& cache, Float size, const std::string& text) override;
MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer<AbstractGlyphCache> doCreateGlyphCache() override;
MAGNUM_MAGNUMFONT_LOCAL Containers::Pointer<AbstractLayouter> 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

29
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 $<TARGET_FILE_DIR:MagnumFontGLTest>/configure.h
file(GENERATE OUTPUT $<TARGET_FILE_DIR:MagnumFontTest>/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_FILE_DIR:MagnumFontGLTest>)
target_include_directories(MagnumFontTest PRIVATE $<TARGET_FILE_DIR:MagnumFontTest>)
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 $<TARGET_FILE_DIR:MagnumFontTest>)
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")

74
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<AbstractFont> 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<AbstractFont> 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<AbstractFont> 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<AbstractFont> 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<GlyphCache> cache = font->createGlyphCache();
Containers::Pointer<AbstractGlyphCache> cache = font->createGlyphCache();
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(cache);

135
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š <mosra@centrum.cz>
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 <sstream>
#include <Corrade/Utility/Directory.h>
#include <Corrade/TestSuite/Tester.h>
#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<Trade::AbstractImporter> _importerManager{"nonexistent"};
PluginManager::Manager<AbstractFont> _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<AbstractFont> 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<AbstractFont> 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<AbstractFont> 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)

2
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()

10
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<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter::doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const {
std::vector<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> 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<std::pair<std::string, Containers::Array<char>>> out;
out.emplace_back(filename + ".conf", std::move(confData));
@ -115,4 +113,4 @@ std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter
}}
CORRADE_PLUGIN_REGISTER(MagnumFontConverter, Magnum::Text::MagnumFontConverter,
"cz.mosra.magnum.Text.AbstractFontConverter/0.1.2")
"cz.mosra.magnum.Text.AbstractFontConverter/0.2")

8
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<std::pair<std::string, Containers::Array<char>>> doExportFontToData(AbstractFont& font, GlyphCache& cache, const std::string& filename, const std::u32string& characters) const override;
MAGNUM_MAGNUMFONTCONVERTER_LOCAL std::vector<std::pair<std::string, Containers::Array<char>>> doExportFontToData(AbstractFont& font, AbstractGlyphCache& cache, const std::string& filename, const std::u32string& characters) const override;
};
}}

16
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 $<TARGET_FILE_DIR:MagnumFontConverterGLTest>/configure.h
file(GENERATE OUTPUT $<TARGET_FILE_DIR:MagnumFontConverterTest>/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_FILE_DIR:MagnumFontConverterGLTest>)
target_include_directories(MagnumFontConverterTest PRIVATE $<TARGET_FILE_DIR:MagnumFontConverterTest>)
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")

38
src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterGLTest.cpp → src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp

@ -23,15 +23,15 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Utility/Directory.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/File.h>
#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<Trade::AbstractImporter> _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<AbstractLayouter> doLayout(const GlyphCache&, Float, const std::string&) { return nullptr; }
Containers::Pointer<AbstractLayouter> 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<char>{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<Trade::AbstractImporter> importer = _importerManager.instantiate("TgaImporter");
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.tga")));
Containers::Optional<Trade::ImageData2D> 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)
Loading…
Cancel
Save