mirror of https://github.com/mosra/magnum.git
Browse Source
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
37 changed files with 1370 additions and 425 deletions
@ -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{{}}); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -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 |
||||||
@ -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) |
||||||
@ -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) |
||||||
Loading…
Reference in new issue