Browse Source

MagnumFont{,Converter}: port to the new AbstractGlyphCache API.

The internals are still rather ew, that's for another time, but the goal
here was to make it compile and correctly handle the new variability in
created and passed glyph cache instances. In particular:

 - The MagnumFont createGlyphCache() now uploads the texture image
   directly, skipping a copy through the CPU-side image which may not
   have a size matching the input image. That's kind of nasty (and too
   tied to OpenGL), but will be cleaned up once the GlyphCache APIs are
   fixed to allow this in a nicer way.
 - The MagnumFontConverter will now correctly select a font if the cache
   contains more than one, properly deal with glyph caches that either
   do or not do image processing, and will fail for array glyph caches
   as that's not supported by the current format. It also now has an
   early special-case handling for glyph caches with processed images,
   where the actual image may have different size (and possibly format),
   to match what MagnumFont expects.
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
4c9fc15962
  1. 57
      src/MagnumPlugins/MagnumFont/MagnumFont.cpp
  2. 2
      src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt
  3. 121
      src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp
  4. 64
      src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp
  5. 39
      src/MagnumPlugins/MagnumFont/Test/font-processed.conf
  6. BIN
      src/MagnumPlugins/MagnumFont/Test/font-processed.tga
  7. BIN
      src/MagnumPlugins/MagnumFont/Test/font.tga
  8. 60
      src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp
  9. 8
      src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h
  10. 2
      src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt
  11. 329
      src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp

57
src/MagnumPlugins/MagnumFont/MagnumFont.cpp

@ -26,6 +26,7 @@
#include "MagnumFont.h"
#include <sstream>
#include <unordered_map>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
@ -64,13 +65,14 @@ struct MagnumFont::Data {
namespace {
class MagnumFontLayouter: public AbstractLayouter {
public:
explicit MagnumFontLayouter(const Containers::StridedArrayView1D<const Vector2>& glyphAdvance, const AbstractGlyphCache& cache, Float fontSize, Float textSize, Containers::Array<UnsignedInt>&& glyphs);
explicit MagnumFontLayouter(const Containers::StridedArrayView1D<const Vector2>& glyphAdvance, const AbstractGlyphCache& cache, UnsignedInt fontId, Float fontSize, Float textSize, Containers::Array<UnsignedInt>&& glyphs);
private:
Containers::Triple<Range2D, Range2D, Vector2> doRenderGlyph(UnsignedInt i) override;
const Containers::StridedArrayView1D<const Vector2> _glyphAdvance;
const AbstractGlyphCache& _cache;
const UnsignedInt _fontId;
const Float _fontSize, _textSize;
const Containers::Array<UnsignedInt> _glyphs;
};
@ -173,18 +175,46 @@ Containers::Pointer<AbstractGlyphCache> MagnumFont::doCreateGlyphCache() {
_opened->conf.value<Vector2i>("originalImageSize"),
_opened->image->size(),
_opened->conf.value<Vector2i>("padding")};
cache->setImage({}, *_opened->image);
/* Copy the opened image data directly to the GL texture because (unlike
image()) it matches the actual image size if it differs from
originalImageSize. A potential other way would be to create a
DistanceFieldGlyphCache instead, and call setDistanceFieldImage() on it,
but the font file itself doesn't contain any info about whether it
actually is a distance field, so that would be not really any better. */
/** @todo clean this up once there's a way to upload the processed image
directly from the base class */
cache->texture().setSubImage(0, {}, *_opened->image);
/* Fill glyph map */
const std::vector<Utility::ConfigurationGroup*> glyphs = _opened->conf.groups("glyph");
for(std::size_t i = 0; i != glyphs.size(); ++i)
cache->insert(i, glyphs[i]->value<Vector2i>("position"), glyphs[i]->value<Range2Di>("rectangle"));
/* Set the global invalid glyph to the same as the per-font invalid
glyph. */
if(!glyphs.empty())
cache->setInvalidGlyph(glyphs[0]->value<Vector2i>("position"), glyphs[0]->value<Range2Di>("rectangle"));
/* Add a font, fill the glyph map */
const UnsignedInt fontId = cache->addFont(glyphs.size(), this);
for(std::size_t i = 0; i < glyphs.size(); ++i)
cache->addGlyph(fontId, i, glyphs[i]->value<Vector2i>("position"), glyphs[i]->value<Range2Di>("rectangle"));
/* GCC 4.8 needs extra help here */
return Containers::Pointer<AbstractGlyphCache>{Utility::move(cache)};
}
Containers::Pointer<AbstractLayouter> MagnumFont::doLayout(const AbstractGlyphCache& cache, const Float size, const Containers::StringView text) {
/* Not yet, at least */
if(cache.size().z() != 1) {
Error{} << "Text::MagnumFont::layout(): array glyph caches are not supported";
return {};
}
/* Find this font in the cache */
Containers::Optional<UnsignedInt> fontId = cache.findFont(this);
if(!fontId) {
Error{} << "Text::MagnumFont::layout(): font not found among" << cache.fontCount() << "fonts in passed glyph cache";
return {};
}
/* Get glyph codes from characters */
Containers::Array<UnsignedInt> glyphs;
arrayReserve(glyphs, text.size());
@ -195,25 +225,26 @@ Containers::Pointer<AbstractLayouter> MagnumFont::doLayout(const AbstractGlyphCa
i = codepointNext.second();
}
return Containers::pointer<MagnumFontLayouter>(stridedArrayView(_opened->glyphs).slice(&Data::Glyph::advance), cache, this->size(), size, Utility::move(glyphs));
return Containers::pointer<MagnumFontLayouter>(stridedArrayView(_opened->glyphs).slice(&Data::Glyph::advance), cache, *fontId, this->size(), size, Utility::move(glyphs));
}
namespace {
MagnumFontLayouter::MagnumFontLayouter(const Containers::StridedArrayView1D<const Vector2>& glyphAdvance, const AbstractGlyphCache& cache, const Float fontSize, const Float textSize, Containers::Array<UnsignedInt>&& glyphs): AbstractLayouter{UnsignedInt(glyphs.size())}, _glyphAdvance{glyphAdvance}, _cache(cache), _fontSize{fontSize}, _textSize{textSize}, _glyphs{Utility::move(glyphs)} {}
MagnumFontLayouter::MagnumFontLayouter(const Containers::StridedArrayView1D<const Vector2>& glyphAdvance, const AbstractGlyphCache& cache, const UnsignedInt fontId, const Float fontSize, const Float textSize, Containers::Array<UnsignedInt>&& glyphs): AbstractLayouter{UnsignedInt(glyphs.size())}, _glyphAdvance{glyphAdvance}, _cache(cache), _fontId{fontId}, _fontSize{fontSize}, _textSize{textSize}, _glyphs{Utility::move(glyphs)} {}
Containers::Triple<Range2D, Range2D, Vector2> MagnumFontLayouter::doRenderGlyph(const UnsignedInt i) {
/* Position of the texture in the resulting glyph, texture coordinates */
Vector2i position;
Range2Di rectangle;
std::tie(position, rectangle) = _cache[_glyphs[i]];
/* Offset of the glyph rectangle relative to the cursor, layer, texture
coordinates. We checked that the glyph cache is 2D in doLayout() so the
layer can be ignored. */
const Containers::Triple<Vector2i, Int, Range2Di> glyph = _cache.glyph(_fontId, _glyphs[i]);
CORRADE_INTERNAL_ASSERT(glyph.second() == 0);
/* Normalized texture coordinates */
const auto textureCoordinates = Range2D(rectangle).scaled(1.0f/Vector2(_cache.textureSize()));
const auto textureCoordinates = Range2D{glyph.third()}.scaled(1.0f/Vector2{_cache.size().xy()});
/* Quad rectangle, computed from texture rectangle, denormalized to
requested text size */
const auto quadRectangle = Range2D(Range2Di::fromSize(position, rectangle.size())).scaled(Vector2(_textSize/_fontSize));
const auto quadRectangle = Range2D{Range2Di::fromSize(glyph.first(), glyph.third().size())}.scaled(Vector2{_textSize/_fontSize});
/* Advance for given glyph, denormalized to requested text size */
const Vector2 advance = _glyphAdvance[_glyphs[i]]*(_textSize/_fontSize);

2
src/MagnumPlugins/MagnumFont/Test/CMakeLists.txt

@ -72,6 +72,8 @@ if(MAGNUM_BUILD_GL_TESTS)
MagnumTrade
MagnumOpenGLTester
FILES
font-processed.conf
font-processed.tga
font.conf
font-empty.conf
font.tga)

121
src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp

@ -24,8 +24,10 @@
*/
#include <sstream>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once AbstractFont is <string>-free */
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Path.h>
#include "Magnum/Image.h"
@ -35,6 +37,7 @@
#ifdef MAGNUM_TARGET_GLES
#include "Magnum/DebugTools/TextureImage.h"
#endif
#include "Magnum/Math/Range.h"
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/GlyphCache.h"
@ -48,6 +51,7 @@ struct MagnumFontGLTest: GL::OpenGLTester {
explicit MagnumFontGLTest();
void createGlyphCache();
void createGlyphCacheProcessedImage();
void createGlyphCacheNoGlyphs();
/* Explicitly forbid system-wide plugin dependencies */
@ -57,6 +61,7 @@ struct MagnumFontGLTest: GL::OpenGLTester {
MagnumFontGLTest::MagnumFontGLTest() {
addTests({&MagnumFontGLTest::createGlyphCache,
&MagnumFontGLTest::createGlyphCacheProcessedImage,
&MagnumFontGLTest::createGlyphCacheNoGlyphs});
/* Load the plugins directly from the build tree. Otherwise they're either
@ -81,16 +86,28 @@ void MagnumFontGLTest::createGlyphCache() {
CORRADE_VERIFY(cache);
MAGNUM_VERIFY_NO_GL_ERROR();
/* The font should associate itself with the cache */
CORRADE_COMPARE(cache->fontCount(), 1);
CORRADE_COMPARE(cache->findFont(font.get()), 0);
/* Verify glyph contents */
CORRADE_COMPARE(cache->glyphCount(), 4);
CORRADE_COMPARE((*cache)[0], std::make_pair(
CORRADE_COMPARE(cache->glyphCount(), 5);
CORRADE_COMPARE(cache->fontGlyphCount(0), 4);
CORRADE_COMPARE(cache->glyph(0), Containers::triple(
Vector2i{-16, -8},
0,
Range2Di{{0, 0}, {32, 16}}));
CORRADE_COMPARE(cache->glyph(0, 0), Containers::triple(
Vector2i{-16, -8},
0,
Range2Di{{0, 0}, {32, 16}}));
CORRADE_COMPARE((*cache)[font->glyphId(U'W')], std::make_pair(
CORRADE_COMPARE(cache->glyph(0, font->glyphId(U'W')), Containers::triple(
Vector2i{9, 26},
0,
Range2Di{{0, 4}, {40, 64}}));
CORRADE_COMPARE((*cache)[font->glyphId(U'e')], std::make_pair(
CORRADE_COMPARE(cache->glyph(0, font->glyphId(U'e')), Containers::triple(
Vector2i{9, 4},
0,
Range2Di{{20, 0}, {128, 48}}));
/* ě has deliberately the same glyph data as e */
UnsignedInt eId = font->glyphId(
@ -104,25 +121,105 @@ void MagnumFontGLTest::createGlyphCache() {
U'ě'
#endif
);
CORRADE_COMPARE((*cache)[eId], std::make_pair(
CORRADE_COMPARE(cache->glyph(0, eId), Containers::triple(
Vector2i{9, 4},
0,
Range2Di{{20, 0}, {128, 48}}));
if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents");
#ifdef MAGNUM_TARGET_GLES2
CORRADE_SKIP("Luminance format used on GLES2 isn't usable for framebuffer reading, can't verify texture contents.");
#else
/* Verify the actual texture. It should be the image file verbatim. On GLES
we cannot really verify that the size matches, but at least
something. */
#ifndef MAGNUM_TARGET_GLES
Image2D image = static_cast<GlyphCache&>(*cache).texture().image(0, {PixelFormat::R8Unorm});
#else
Image2D image = DebugTools::textureSubImage(static_cast<GlyphCache&>(*cache).texture(), 0, {{}, {8, 4}}, {PixelFormat::R8Unorm});
Image2D image = DebugTools::textureSubImage(static_cast<GlyphCache&>(*cache).texture(), 0, {{}, {128, 64}}, {PixelFormat::R8Unorm});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(image,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.tga"),
DebugTools::CompareImageToFile{_importerManager});
#endif
}
void MagnumFontGLTest::createGlyphCacheProcessedImage() {
/* Compared to createGlyphCache(), this tests the case where the image size
is different from the actual size to which glyphs are positioned */
Containers::Pointer<AbstractFont> font = _fontManager.instantiate("MagnumFont");
CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font-processed.conf"), 0.0f));
Containers::Pointer<AbstractGlyphCache> cache = font->createGlyphCache();
CORRADE_VERIFY(cache);
MAGNUM_VERIFY_NO_GL_ERROR();
/* The font should associate itself with the cache */
CORRADE_COMPARE(cache->fontCount(), 1);
CORRADE_COMPARE(cache->findFont(font.get()), 0);
/* Verify glyph contents */
CORRADE_COMPARE(cache->glyphCount(), 5);
CORRADE_COMPARE(cache->fontGlyphCount(0), 4);
CORRADE_COMPARE(cache->glyph(0), Containers::triple(
Vector2i{-16, -8},
0,
Range2Di{{0, 0}, {32, 16}}));
CORRADE_COMPARE(cache->glyph(0, 0), Containers::triple(
Vector2i{-16, -8},
0,
Range2Di{{0, 0}, {32, 16}}));
CORRADE_COMPARE(cache->glyph(0, font->glyphId(U'W')), Containers::triple(
Vector2i{9, 26},
0,
Range2Di{{0, 4}, {40, 64}}));
CORRADE_COMPARE(cache->glyph(0, font->glyphId(U'e')), Containers::triple(
Vector2i{9, 4},
0,
Range2Di{{20, 0}, {128, 48}}));
/* ě has deliberately the same glyph data as e */
UnsignedInt eId = font->glyphId(
/* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals
but it does it regular strings. Still a problem in MSVC 2022, what a
trash fire, can't you just give up on those codepage insanities
already, ffs?! */
#if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)
U'\u011B'
#else
U'ě'
#endif
);
CORRADE_COMPARE(cache->glyph(0, eId), Containers::triple(
Vector2i{9, 4},
0,
Range2Di{{20, 0}, {128, 48}}));
if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents");
#ifdef MAGNUM_TARGET_GLES2
CORRADE_SKIP("Luminance format used on GLES2 isn't usable for framebuffer reading, can't verify texture contents.");
#else
/* Verify the actual texture. It should be the image file verbatim. On GLES
we cannot really verify that the size matches, but at least
something. */
#ifndef MAGNUM_TARGET_GLES
Image2D image = static_cast<GlyphCache&>(*cache).texture().image(0, {PixelFormat::R8Unorm});
#else
Image2D image = DebugTools::textureSubImage(static_cast<GlyphCache&>(*cache).texture(), 0, {{}, {8, 4}}, {PixelFormat::R8Unorm});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(image,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font-processed.tga"),
DebugTools::CompareImageToFile{_importerManager});
#endif
}
void MagnumFontGLTest::createGlyphCacheNoGlyphs() {
@ -134,11 +231,17 @@ void MagnumFontGLTest::createGlyphCacheNoGlyphs() {
CORRADE_VERIFY(cache);
MAGNUM_VERIFY_NO_GL_ERROR();
/* The font should associate itself with the cache even in this case */
CORRADE_COMPARE(cache->fontCount(), 1);
CORRADE_COMPARE(cache->findFont(font.get()), 0);
/* There's just the empty glyph added by the cache itself, nothing else */
CORRADE_COMPARE(cache->glyphCount(), 1);
CORRADE_COMPARE((*cache)[0], std::make_pair(
Vector2i{0, 0},
Range2Di{{0, 0}, {0, 0}}));
CORRADE_COMPARE(cache->fontGlyphCount(0), 0);
CORRADE_COMPARE(cache->glyph(0), Containers::triple(
Vector2i{},
0,
Range2Di{}));
/* Not testing the image as there's no special codepath taken for it if
there are no glyphs */

64
src/MagnumPlugins/MagnumFont/Test/MagnumFontTest.cpp

@ -24,6 +24,7 @@
*/
#include <sstream>
#include <unordered_map>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once AbstractFont is <string>-free */
@ -33,6 +34,8 @@
#include <Corrade/Utility/Path.h>
#include "Magnum/FileCallback.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/Math/Range.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractGlyphCache.h"
#include "Magnum/Trade/AbstractImporter.h"
@ -48,6 +51,8 @@ struct MagnumFontTest: TestSuite::Tester {
void properties();
void layout();
void layoutNoGlyphsInCache();
void layoutNoFontInCache();
void layoutArrayCache();
void fileCallbackImage();
void fileCallbackImageNotFound();
@ -73,6 +78,8 @@ MagnumFontTest::MagnumFontTest() {
Containers::arraySize(LayoutData));
addTests({&MagnumFontTest::layoutNoGlyphsInCache,
&MagnumFontTest::layoutNoFontInCache,
&MagnumFontTest::layoutArrayCache,
&MagnumFontTest::fileCallbackImage,
&MagnumFontTest::fileCallbackImageNotFound});
@ -143,11 +150,14 @@ void MagnumFontTest::layout() {
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}});
} cache{PixelFormat::R8Unorm, Vector2i{256}};
UnsignedInt fontId = cache.addFont(20, font.get());
cache.addGlyph(fontId, font->glyphId(U'W'), {25, 34}, {{0, 8}, {16, 128}});
cache.addGlyph(fontId, font->glyphId(U'e'), {25, 12}, {{16, 4}, {64, 32}});
/* ě has deliberately the same glyph data as e */
cache.insert(font->glyphId(
cache.addGlyph(fontId, font->glyphId(
/* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals
but it does it regular strings. Still a problem in MSVC 2022, what a
trash fire, can't you just give up on those codepage insanities
@ -194,13 +204,16 @@ void MagnumFontTest::layoutNoGlyphsInCache() {
CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f));
/* Tests the case where createGlyphCache() was accidentally not called */
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{Vector2i{256}};
} cache{PixelFormat::R8Unorm, Vector2i{256}};
/* Add a font that is associated with this one but createGlyphCache() was
actually not called for it */
cache.addFont(15, font.get());
auto layouter = font->layout(cache, 0.5f, "Wave");
CORRADE_VERIFY(layouter);
@ -229,6 +242,45 @@ void MagnumFontTest::layoutNoGlyphsInCache() {
CORRADE_COMPARE(cursorPosition, Vector2(0.375f, 0.0f));
}
void MagnumFontTest::layoutNoFontInCache() {
Containers::Pointer<AbstractFont> font = _fontManager.instantiate("MagnumFont");
CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f));
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R8Unorm, Vector2i{256}};
/* Add a font that isn't associated with this one */
cache.addFont(15);
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!font->layout(cache, 0.5f, "Wave"));
CORRADE_COMPARE(out.str(), "Text::MagnumFont::layout(): font not found among 1 fonts in passed glyph cache\n");
}
void MagnumFontTest::layoutArrayCache() {
Containers::Pointer<AbstractFont> font = _fontManager.instantiate("MagnumFont");
CORRADE_VERIFY(font->openFile(Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"), 0.0f));
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R8Unorm, {256, 128, 3}};
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!font->layout(cache, 0.5f, "Wave"));
CORRADE_COMPARE(out.str(), "Text::MagnumFont::layout(): array glyph caches are not supported\n");
}
void MagnumFontTest::fileCallbackImage() {
Containers::Pointer<AbstractFont> font = _fontManager.instantiate("MagnumFont");
CORRADE_VERIFY(font->features() & FontFeature::FileCallback);

39
src/MagnumPlugins/MagnumFont/Test/font-processed.conf

@ -0,0 +1,39 @@
version=1
image=font-processed.tga
originalImageSize=128 64
padding=16 8
fontSize=16
ascent=25
descent=-10
lineHeight=39.7333
[char]
unicode=57
glyph=2
[char]
unicode=61
glyph=0
[char]
unicode=65
glyph=1
[char]
unicode=76
glyph=0
[char]
unicode=11B
glyph=3
[glyph]
advance=8 0
position=0 0
rectangle=16 8 16 8
[glyph]
advance=12 0
position=25 12
rectangle=36 8 112 40
[glyph]
advance=23 0
position=25 34
rectangle=16 12 24 56
[glyph]
advance=12 0
position=25 12
rectangle=36 8 112 40

BIN
src/MagnumPlugins/MagnumFont/Test/font-processed.tga

Binary file not shown.

BIN
src/MagnumPlugins/MagnumFont/Test/font.tga

Binary file not shown.

60
src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.cpp

@ -27,9 +27,11 @@
#include <algorithm> /* std::sort() */
#include <sstream>
#include <unordered_map>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Configuration.h>
#include <Corrade/Utility/Path.h>
@ -52,8 +54,24 @@ FontConverterFeatures MagnumFontConverter::doFeatures() 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 {
if(!(cache.features() & GlyphCacheFeature::ImageDownload)) {
Error{} << "Text::MagnumFontConverter::exportFontToData(): passed glyph cache doesn't support image download";
if(cache.size().z() != 1) {
Error{} << "Text::MagnumFontConverter::exportFontToData(): exporting array glyph caches is not supported";
return {};
}
if(cache.features() & GlyphCacheFeature::ImageProcessing && !(cache.features() >= GlyphCacheFeature::ProcessedImageDownload)) {
Error{} << "Text::MagnumFontConverter::exportFontToData(): glyph cache has image processing but doesn't support image download";
return {};
}
Containers::Optional<UnsignedInt> fontId = cache.findFont(&font);
#ifdef MAGNUM_BUILD_DEPRECATED
/* Make it work with the old-style glyph cache filling that adds exactly
one font into the cache and doesn't associate any pointer with it */
if(!fontId && cache.fontCount() == 1 && cache.fontPointer(0) == nullptr)
fontId = 0;
#endif
if(!fontId) {
Error{} << "Text::MagnumFontConverter::exportFontToData(): font not found among" << cache.fontCount() << "fonts in passed glyph cache";
return {};
}
@ -61,7 +79,7 @@ std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter
configuration.setValue("version", 1);
configuration.setValue("image", Utility::Path::split(filename).second() + ".tga");
configuration.setValue("originalImageSize", cache.textureSize());
configuration.setValue("originalImageSize", cache.size().xy());
configuration.setValue("padding", cache.padding());
configuration.setValue("fontSize", font.size());
configuration.setValue("ascent", font.ascent());
@ -70,8 +88,11 @@ std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter
/* Get the glyphs and sort them for predictable output */
std::vector<std::pair<UnsignedInt, std::pair<Vector2i, Range2Di>>> sortedGlyphs;
for(const std::pair<const UnsignedInt, std::pair<Vector2i, Range2Di>>& glyph: cache)
sortedGlyphs.emplace_back(glyph);
const Containers::StridedArrayView1D<const Vector2i> offsets = cache.glyphOffsets();
const Containers::StridedArrayView1D<const Range2Di> rectangles = cache.glyphRectangles();
for(UnsignedInt fontGlyphId = 0; fontGlyphId != cache.fontGlyphCount(*fontId); ++fontGlyphId)
if(const UnsignedInt glyphId = cache.glyphId(*fontId, fontGlyphId))
sortedGlyphs.emplace_back(fontGlyphId, std::make_pair(offsets[glyphId], rectangles[glyphId]));
std::sort(sortedGlyphs.begin(), sortedGlyphs.end(),
[](const std::pair<UnsignedInt, std::pair<Vector2i, Range2Di>>& a,
const std::pair<UnsignedInt, std::pair<Vector2i, Range2Di>>& b) {
@ -108,11 +129,15 @@ std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter
from the values so they aren't added twice when using the font later */
/** @todo Some better way to handle this padding stuff */
for(UnsignedInt oldGlyphId: inverseGlyphIdMap) {
std::pair<Vector2i, Range2Di> glyph = cache[oldGlyphId];
/** @todo this branch is messy, clean up; also there's now a
distinction between a cache-global invalid glyph and font-local,
what to do there? */
Containers::Triple<Vector2i, Int, Range2Di> glyph =
oldGlyphId ? cache.glyph(*fontId, oldGlyphId) : cache.glyph(0);
Utility::ConfigurationGroup* group = configuration.addGroup("glyph");
group->setValue("advance", font.glyphAdvance(oldGlyphId));
group->setValue("position", glyph.first+cache.padding());
group->setValue("rectangle", glyph.second.padded(-cache.padding()));
group->setValue("position", glyph.first() + cache.padding());
group->setValue("rectangle", glyph.third().padded(-cache.padding()));
}
std::ostringstream confOut;
@ -121,9 +146,20 @@ std::vector<std::pair<std::string, Containers::Array<char>>> MagnumFontConverter
Containers::Array<char> confData{confStr.size()};
std::copy(confStr.begin(), confStr.end(), confData.begin());
/* Save cache image */
Containers::Optional<Containers::Array<char>> tgaData = Trade::TgaImageConverter().convertToData(cache.image());
if(!tgaData) return {};
/* Save cache image. Either the source image or the processed one if the
cache has image processing. */
Containers::Optional<Containers::Array<char>> tgaData;
if(cache.features() & GlyphCacheFeature::ImageProcessing) {
const Image3D image3 = cache.processedImage();
tgaData = Trade::TgaImageConverter().convertToData(ImageView2D{image3.format(), image3.size().xy(), image3.data()});
} else {
const ImageView3D image3 = cache.image();
tgaData = Trade::TgaImageConverter().convertToData(ImageView2D{image3.format(), image3.size().xy(), image3.data()});
}
if(!tgaData) {
Error{} << "Text::MagnumFontConverter::exportFontToData(): cannot create a TGA image";
return {};
}
std::vector<std::pair<std::string, Containers::Array<char>>> out;
out.emplace_back(filename + ".conf", Utility::move(confData));

8
src/MagnumPlugins/MagnumFontConverter/MagnumFontConverter.h

@ -56,7 +56,13 @@ namespace Magnum { namespace Text {
Expects filename prefix, creates two files, `prefix.conf` and `prefix.tga`. See
@ref MagnumFont for more information about the font. The plugin requires the
passed @ref AbstractGlyphCache to support @ref GlyphCacheFeature::ImageDownload.
passed @ref AbstractGlyphCache to be 2D, have a format compatible with the
@relativeref{Trade,TgaImageConverter} plugin and either not have
@ref GlyphCacheFeature::ImageProcessing or support both
@ref GlyphCacheFeature::ImageProcessing and
@relativeref{GlyphCacheFeature,ProcessedImageDownload}. For a cache with
multiple fonts, @ref AbstractGlyphCache::findFont() is used to match the font
with the passed instance.
@section Text-MagnumFontConverter-usage Usage

2
src/MagnumPlugins/MagnumFontConverter/Test/CMakeLists.txt

@ -60,6 +60,8 @@ corrade_add_test(MagnumFontConverterTest MagnumFontConverterTest.cpp
MagnumText
MagnumTrade
FILES
../../MagnumFont/Test/font-processed.conf
../../MagnumFont/Test/font-processed.tga
../../MagnumFont/Test/font.conf
../../MagnumFont/Test/font.tga
font-empty-cache.conf

329
src/MagnumPlugins/MagnumFontConverter/Test/MagnumFontConverterTest.cpp

@ -26,9 +26,11 @@
#include <sstream>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once AbstractFont is <string>-free */
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove once AbstractFontConverter is STL-free */
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/File.h>
#include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Path.h>
@ -36,6 +38,7 @@
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/DebugTools/CompareImage.h"
#include "Magnum/Math/Range.h"
#include "Magnum/Text/AbstractGlyphCache.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractFontConverter.h"
@ -51,8 +54,16 @@ struct MagnumFontConverterTest: TestSuite::Tester {
explicit MagnumFontConverterTest();
void exportFont();
#ifdef MAGNUM_BUILD_DEPRECATED
void exportFontOldStyleCache();
#endif
void exportFontEmptyCache();
void exportFontNoGlyphCacheImageDownload();
void exportFontImageProcessingGlyphCache();
void exportFontImageProcessingGlyphCacheNoDownload();
void exportFontArrayCache();
void exportFontNotFoundInCache();
void exportFontImageConversionFailed();
/* Explicitly forbid system-wide plugin dependencies */
PluginManager::Manager<Trade::AbstractImageConverter> _imageConverterManager{"nonexistent"};
@ -62,8 +73,16 @@ struct MagnumFontConverterTest: TestSuite::Tester {
MagnumFontConverterTest::MagnumFontConverterTest() {
addTests({&MagnumFontConverterTest::exportFont,
#ifdef MAGNUM_BUILD_DEPRECATED
&MagnumFontConverterTest::exportFontOldStyleCache,
#endif
&MagnumFontConverterTest::exportFontEmptyCache,
&MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload});
&MagnumFontConverterTest::exportFontImageProcessingGlyphCache,
&MagnumFontConverterTest::exportFontImageProcessingGlyphCacheNoDownload,
&MagnumFontConverterTest::exportFontArrayCache,
&MagnumFontConverterTest::exportFontNotFoundInCache,
&MagnumFontConverterTest::exportFontImageConversionFailed});
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree. */
@ -143,19 +162,95 @@ void MagnumFontConverterTest::exportFont() {
MyFont font;
font.openFile({}, {});
/* Create a cache. Two fonts, only the second one should be added. */
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; }
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R8Unorm, {128, 64}, {16, 8}};
/* Override the not found glyph to be in bounds as well */
cache.setInvalidGlyph({}, {{16, 8}, {16, 8}});
/* This font and all its glyphs should be skipped */
UnsignedInt unusedFontId = cache.addFont(56);
cache.addGlyph(unusedFontId, 33, {16, 20}, {{60, 40}, {80, 50}});
UnsignedInt fontId = cache.addFont(25, &font);
cache.addGlyph(fontId, font.glyphId(U'W'), {25, 34}, {{16, 12}, {24, 56}});
cache.addGlyph(fontId, font.glyphId(U'e'), {25, 12}, {{36, 8}, {112, 40}});
/* ě has deliberately the same glyph data as e */
cache.addGlyph(fontId, font.glyphId(
/* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals
but it does it regular strings. Still a problem in MSVC 2022, what a
trash fire, can't you just give up on those codepage insanities
already, ffs?! */
#if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)
U'\u011B'
#else
U'ě'
#endif
), {25, 12}, {{36, 8}, {112, 40}});
/* Set the cache image to some non-trivial contents. Compared to the
exportFontImageProcessingGlyphCache() test the image is 16x bigger, so
do some fancy expansion there. */
const char pixels[]{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
};
for(std::size_t y = 0; y != 16; ++y)
for(std::size_t x = 0; x != 16; ++x)
Utility::copy(
Containers::StridedArrayView2D<const char>{pixels, {4, 8}},
cache.image().pixels<char>()[0].exceptPrefix({y, x}).every({16, 16}));
/* Convert the file */
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
CORRADE_VERIFY(converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Waveě"));
/* Verify font parameters */
CORRADE_COMPARE_AS(confFilename,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.conf"),
TestSuite::Compare::File);
if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents");
/* Verify font image */
CORRADE_COMPARE_WITH(tgaFilename,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.tga"),
DebugTools::CompareImageFile{_importerManager});
}
#ifdef MAGNUM_BUILD_DEPRECATED
void MagnumFontConverterTest::exportFontOldStyleCache() {
/* Like exportFont(), but using the deprecated cache APIs to verify that
the cache contents are still copied the same */
Containers::String confFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.conf");
Containers::String tgaFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font.tga");
/* Remove previously created files */
if(Utility::Path::exists(confFilename))
CORRADE_VERIFY(Utility::Path::remove(confFilename));
if(Utility::Path::exists(tgaFilename))
CORRADE_VERIFY(Utility::Path::remove(tgaFilename));
MyFont font;
font.openFile({}, {});
/* Create a cache the old way, i.e. insert() which results in exactly one
font added and no association with a pointer */
CORRADE_IGNORE_DEPRECATED_PUSH
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
Image2D doImage() override {
return Image2D{PixelFormat::R8Unorm, {8, 4}, Containers::Array<char>{InPlaceInit, {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
}}};
}
} cache{{128, 64}, {16, 8}};
/* Override the not found glyph to be in bounds as well */
cache.insert(0, {}, {{16, 8}, {16, 8}});
@ -173,6 +268,22 @@ void MagnumFontConverterTest::exportFont() {
U'ě'
#endif
), {25, 12}, {{36, 8}, {112, 40}});
CORRADE_IGNORE_DEPRECATED_POP
/* Set the cache image to some non-trivial contents. There's no "old way"
to do this, also compared to the exportFontImageProcessingGlyphCache()
test the image is 16x bigger, so do some fancy expansion there. */
const char pixels[]{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
};
for(std::size_t y = 0; y != 16; ++y)
for(std::size_t x = 0; x != 16; ++x)
Utility::copy(
Containers::StridedArrayView2D<const char>{pixels, {4, 8}},
cache.image().pixels<char>()[0].exceptPrefix({y, x}).every({16, 16}));
/* Convert the file */
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
@ -192,6 +303,7 @@ void MagnumFontConverterTest::exportFont() {
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font.tga"),
DebugTools::CompareImageFile{_importerManager});
}
#endif
void MagnumFontConverterTest::exportFontEmptyCache() {
Containers::String confFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-empty-cache.conf");
@ -208,12 +320,13 @@ void MagnumFontConverterTest::exportFontEmptyCache() {
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageDownload; }
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
Image2D doImage() override {
return Image2D{PixelFormat::R8Unorm, {8, 4}, Containers::Array<char>{ValueInit, 8*4}};
}
} cache{{8, 4}};
} cache{PixelFormat::R8Unorm, {8, 4}};
/* Associate the font with the cache. The case where it's not even that is
tested in exportFontNotFoundInCache() below. */
cache.addFont(0, &font);
/* Convert the file */
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
@ -234,7 +347,104 @@ void MagnumFontConverterTest::exportFontEmptyCache() {
DebugTools::CompareImageFile{_importerManager});
}
void MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload() {
void MagnumFontConverterTest::exportFontImageProcessingGlyphCache() {
/* Like exportFont(), but the image is processed to a 16x smaller one. The
rest stays the same, i.e. the offsets and sizes are still relative to
the original 128x64 image. */
Containers::String confFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-processed.conf");
Containers::String tgaFilename = Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-processed.tga");
/* Remove previously created files */
if(Utility::Path::exists(confFilename))
CORRADE_VERIFY(Utility::Path::remove(confFilename));
if(Utility::Path::exists(tgaFilename))
CORRADE_VERIFY(Utility::Path::remove(tgaFilename));
MyFont font;
font.openFile({}, {});
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ProcessedImageDownload; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
Image3D doProcessedImage() override {
return Image3D{PixelFormat::R8Unorm, {8, 4, 1}, Containers::Array<char>{InPlaceInit, {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v'
}}};
}
} cache{PixelFormat::R8Unorm, {128, 64}, {16, 8}};
/* Override the not found glyph to be in bounds as well */
cache.setInvalidGlyph({}, {{16, 8}, {16, 8}});
UnsignedInt fontId = cache.addFont(25, &font);
cache.addGlyph(fontId, font.glyphId(U'W'), {25, 34}, {{16, 12}, {24, 56}});
cache.addGlyph(fontId, font.glyphId(U'e'), {25, 12}, {{36, 8}, {112, 40}});
/* ě has deliberately the same glyph data as e */
cache.addGlyph(fontId, font.glyphId(
/* MSVC (but not clang-cl) doesn't support UTF-8 in char32_t literals
but it does it regular strings. Still a problem in MSVC 2022, what a
trash fire, can't you just give up on those codepage insanities
already, ffs?! */
#if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG)
U'\u011B'
#else
U'ě'
#endif
), {25, 12}, {{36, 8}, {112, 40}});
/* Convert the file */
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
CORRADE_VERIFY(converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font-processed"), "Waveě"));
/* Verify font parameters */
CORRADE_COMPARE_AS(confFilename,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font-processed.conf"),
TestSuite::Compare::File);
if(!(_importerManager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found, not testing glyph cache contents");
/* Verify font image */
CORRADE_COMPARE_WITH(tgaFilename,
Utility::Path::join(MAGNUMFONT_TEST_DIR, "font-processed.tga"),
DebugTools::CompareImageFile{_importerManager});
}
void MagnumFontConverterTest::exportFontImageProcessingGlyphCacheNoDownload() {
struct: AbstractFont {
/* Supports neither file nor data opening */
FontFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
UnsignedInt doGlyphId(char32_t) override { return {}; }
Vector2 doGlyphSize(UnsignedInt) override { return {}; }
Vector2 doGlyphAdvance(UnsignedInt) override { return {}; }
Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache&, Float, Containers::StringView) override {
return nullptr;
}
} font;
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return GlyphCacheFeature::ImageProcessing; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R8Unorm, {100, 100}};
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
std::ostringstream out;
Error redirectError{&out};
converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave");
CORRADE_COMPARE(out.str(), "Text::MagnumFontConverter::exportFontToData(): glyph cache has image processing but doesn't support image download\n");
}
void MagnumFontConverterTest::exportFontArrayCache() {
struct: AbstractFont {
/* Supports neither file nor data opening */
FontFeatures doFeatures() const override { return {}; }
@ -254,14 +464,91 @@ void MagnumFontConverterTest::exportFontNoGlyphCacheImageDownload() {
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{{100, 100}};
} cache{PixelFormat::R8Unorm, {100, 100, 2}};
cache.addFont(15, &font);
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
std::ostringstream out;
Error redirectError{&out};
converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave");
CORRADE_COMPARE(out.str(), "Text::MagnumFontConverter::exportFontToData(): exporting array glyph caches is not supported\n");
}
void MagnumFontConverterTest::exportFontNotFoundInCache() {
struct: AbstractFont {
/* Supports neither file nor data opening */
FontFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
UnsignedInt doGlyphId(char32_t) override { return {}; }
Vector2 doGlyphSize(UnsignedInt) override { return {}; }
Vector2 doGlyphAdvance(UnsignedInt) override { return {}; }
Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache&, Float, Containers::StringView) override {
return nullptr;
}
} font1, font2;
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R8Unorm, {100, 100}};
cache.addFont(15, &font2);
cache.addFont(33);
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
std::ostringstream out;
Error redirectError{&out};
converter->exportFontToFile(font1, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave");
CORRADE_COMPARE(out.str(), "Text::MagnumFontConverter::exportFontToData(): font not found among 2 fonts in passed glyph cache\n");
}
void MagnumFontConverterTest::exportFontImageConversionFailed() {
struct: AbstractFont {
FontFeatures doFeatures() const override { return {}; }
void doClose() override { _opened = false; }
bool doIsOpened() const override { return _opened; }
Properties doOpenFile(Containers::StringView, Float) override {
_opened = true;
return {16.0f, 25.0f, -10.0f, 39.7333f, 3};
}
UnsignedInt doGlyphId(char32_t) override { return {}; }
Vector2 doGlyphSize(UnsignedInt) override { return {}; }
Vector2 doGlyphAdvance(UnsignedInt) override { return {}; }
Containers::Pointer<AbstractLayouter> doLayout(const AbstractGlyphCache&, Float, Containers::StringView) override {
return nullptr;
}
private:
bool _opened;
} font;
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
GlyphCacheFeatures doFeatures() const override { return {}; }
void doSetImage(const Vector2i&, const ImageView2D&) override {}
} cache{PixelFormat::R32F, {100, 100}};
font.openFile({}, 0.0f);
cache.addFont(15, &font);
Containers::Pointer<AbstractFontConverter> converter = _fontConverterManager.instantiate("MagnumFontConverter");
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave"));
CORRADE_COMPARE(out.str(), "Text::MagnumFontConverter::exportFontToData(): passed glyph cache doesn't support image download\n");
converter->exportFontToFile(font, cache, Utility::Path::join(MAGNUMFONTCONVERTER_TEST_WRITE_DIR, "font"), "Wave");
CORRADE_COMPARE(out.str(),
"Trade::TgaImageConverter::convertToData(): unsupported pixel format PixelFormat::R32F\n"
"Text::MagnumFontConverter::exportFontToData(): cannot create a TGA image\n");
}
}}}}

Loading…
Cancel
Save