Browse Source

Text: move [DistanceField]GlyphCacheGL internals to the state struct.

Same approach as done e.g. in the Ui library -- taking advantage of the
base class already allocating an internal state struct, deriving from it
and putting the state there instead of either having it as a class
member (at the cost of extra header dependency) or as a separate state
struct (at the cost of extra allocation). The only downside is a virtual
destructor in the state struct, but compared to the alternatives that's
completely fine.
pull/674/head
Vladimír Vondruš 1 year ago
parent
commit
afe56a5d49
  1. 56
      src/Magnum/Text/AbstractGlyphCache.cpp
  2. 13
      src/Magnum/Text/AbstractGlyphCache.h
  3. 3
      src/Magnum/Text/CMakeLists.txt
  4. 30
      src/Magnum/Text/DistanceFieldGlyphCacheGL.cpp
  5. 9
      src/Magnum/Text/DistanceFieldGlyphCacheGL.h
  6. 49
      src/Magnum/Text/GlyphCacheGL.cpp
  7. 17
      src/Magnum/Text/GlyphCacheGL.h
  8. 91
      src/Magnum/Text/Implementation/abstractGlyphCacheState.h
  9. 53
      src/Magnum/Text/Implementation/glyphCacheGLState.h
  10. 1
      src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp
  11. 4
      src/Magnum/Text/Test/GlyphCacheGLTest.cpp
  12. 4
      src/Magnum/Text/Test/RendererGLTest.cpp
  13. 1
      src/MagnumPlugins/MagnumFont/Test/MagnumFontGLTest.cpp

56
src/Magnum/Text/AbstractGlyphCache.cpp

@ -26,18 +26,15 @@
#include "AbstractGlyphCache.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/Triple.h>
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/Math/Range.h"
#include "Magnum/TextureTools/Atlas.h"
#include "Magnum/Text/Implementation/abstractGlyphCacheState.h"
#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Containers/ArrayViewStl.h>
@ -70,51 +67,6 @@ Debug& operator<<(Debug& debug, const GlyphCacheFeatures value) {
});
}
struct AbstractGlyphCache::State {
explicit State(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding);
Image3D image;
TextureTools::AtlasLandfill atlas;
PixelFormat processedFormat;
Vector2i processedSize;
Vector2i padding;
/* 0/4 bytes free */
/* First element is glyph position relative to a point on the baseline,
second layer in the texture atlas, third a region in the atlas
slice. Index of the item is ID of the glyph in the cache, refered to
from the fontGlyphMapping array. Index 0 is reserved for an invalid
glyph. */
Containers::Array<Containers::Triple<Vector2i, Int, Range2Di>> glyphs;
/* `fontRanges[i]` to `fontRanges[i + 1]` is the range in
`fontGlyphMapping` containing a mapping for glyphs from font `i`,
`fontGlyphMapping[fontRanges[i]] + j` is then mapping from glyph ID `j`
from font `i` to index in the `glyphs` array, or is 0 if given
glyph isn't present in the cache (which then maps to the invalid
glyph). */
struct Font {
UnsignedInt offset;
/* 4 bytes free on 64b, but not so critical I think */
const AbstractFont* pointer;
};
Containers::Array<Font> fonts;
/* With an assumption that majority of font glyphs get put into a cache,
this achieves O(1) mapping from a font ID + font-specific glyph ID pair
to a cache-global glyph ID with far less overhead than a hashmap would,
and much less memory used as well compared to storing a key, value and a
hash for each mapping entry.
Another assumption is that there's no more than 64k glyphs in total,
which makes the mapping save half memory compared to storing 32-bit
ints. 64K glyphs is enough to fill a 4K texture with 16x16 glyphs, which
seems enough for now. It however might get reached at some point in
practice, in which case the type would simply get changed to a 32-bit
one (and the assertion in addGlyph() then removed). */
Containers::Array<UnsignedShort> fontGlyphMapping;
};
AbstractGlyphCache::State::State(const PixelFormat format, const Vector3i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): image{format, size, Containers::Array<char>{ValueInit, 4*((pixelFormatSize(format)*size.x() + 3)/4)*size.y()*size.z()}}, atlas{NoCreate}, processedFormat{processedFormat}, processedSize{processedSize}, padding{padding} {
CORRADE_ASSERT(size.product(),
"Text::AbstractGlyphCache: expected non-zero size, got" << Debug::packed << size, );
@ -137,6 +89,8 @@ AbstractGlyphCache::State::State(const PixelFormat format, const Vector3i& size,
arrayAppend(fonts, InPlaceInit, 0u, nullptr);
}
AbstractGlyphCache::State::~State() = default;
AbstractGlyphCache::AbstractGlyphCache(const PixelFormat format, const Vector3i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): _state{InPlaceInit, format, size, processedFormat, processedSize, padding} {}
AbstractGlyphCache::AbstractGlyphCache(const PixelFormat format, const Vector3i& size, const PixelFormat processedFormat, const Vector2i& processedSize): AbstractGlyphCache{format, size, processedFormat, processedSize, Vector2i{1}} {}
@ -159,6 +113,8 @@ AbstractGlyphCache::AbstractGlyphCache(const Vector2i& size, const Vector2i& pad
AbstractGlyphCache::AbstractGlyphCache(const Vector2i& size): AbstractGlyphCache{PixelFormat::R8Unorm, size, Vector2i{1}} {}
#endif
AbstractGlyphCache::AbstractGlyphCache(Containers::Pointer<State>&& state) noexcept: _state{Utility::move(state)} {}
AbstractGlyphCache::AbstractGlyphCache(NoCreateT) noexcept {}
AbstractGlyphCache::AbstractGlyphCache(AbstractGlyphCache&&) noexcept = default;

13
src/Magnum/Text/AbstractGlyphCache.h

@ -980,6 +980,17 @@ class MAGNUM_TEXT_EXPORT AbstractGlyphCache {
CORRADE_DEPRECATED("use glyph() instead") std::pair<Vector2i, Range2Di> operator[](UnsignedInt glyphId) const;
#endif
#ifndef DOXYGEN_GENERATING_OUTPUT
protected:
#else
private:
#endif
struct State;
Containers::Pointer<State> _state;
/* Used by GlyphCacheGL, GlyphCacheArrayGL */
explicit AbstractGlyphCache(Containers::Pointer<State>&& state) noexcept;
private:
/** @brief Implementation for @ref features() */
virtual GlyphCacheFeatures doFeatures() const = 0;
@ -1040,8 +1051,6 @@ class MAGNUM_TEXT_EXPORT AbstractGlyphCache {
*/
virtual void doSetProcessedImage(const Vector2i& offset, const ImageView2D& image);
struct State;
Containers::Pointer<State> _state;
};
}}

3
src/Magnum/Text/CMakeLists.txt

@ -61,6 +61,7 @@ set(MagnumText_HEADERS
set(MagnumText_PRIVATE_HEADERS
Implementation/printFourCC.h
Implementation/abstractGlyphCacheState.h
Implementation/rendererState.h)
if(MAGNUM_TARGET_GL)
@ -72,6 +73,8 @@ if(MAGNUM_TARGET_GL)
DistanceFieldGlyphCacheGL.h
GlyphCacheGL.h
RendererGL.h)
list(APPEND MagnumText_PRIVATE_HEADERS
Implementation/glyphCacheGLState.h)
if(MAGNUM_BUILD_DEPRECATED)
list(APPEND MagnumText_HEADERS
DistanceFieldGlyphCache.h

30
src/Magnum/Text/DistanceFieldGlyphCacheGL.cpp

@ -39,11 +39,19 @@
#endif
#include "Magnum/GL/TextureFormat.h"
#include "Magnum/Math/Range.h"
#include "Magnum/Text/Implementation/glyphCacheGLState.h"
#include "Magnum/TextureTools/DistanceFieldGL.h"
namespace Magnum { namespace Text {
DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(const Vector2i& size, const Vector2i& processedSize, const UnsignedInt radius):
GlyphCacheGL{PixelFormat::R8Unorm, size,
struct DistanceFieldGlyphCacheGL::State: GlyphCacheGL::State {
explicit State(const Vector2i& size, const Vector2i& processedSize, UnsignedInt radius);
TextureTools::DistanceFieldGL distanceField;
};
DistanceFieldGlyphCacheGL::State::State(const Vector2i& size, const Vector2i& processedSize, const UnsignedInt radius):
GlyphCacheGL::State{PixelFormat::R8Unorm, size,
#if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_GLES2)
PixelFormat::R8Unorm,
#else
@ -58,7 +66,7 @@ DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(const Vector2i& size, const
PixelFormat::RGBA8Unorm,
#endif
processedSize, Vector2i(radius)},
_distanceField{radius}
distanceField{radius}
{
/* Replicating the assertion from TextureTools::DistanceFieldGL so it gets
checked during construction already instead of only later during the
@ -76,7 +84,15 @@ DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(const Vector2i& size, const
#endif
}
DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(NoCreateT) noexcept: GlyphCacheGL{NoCreate}, _distanceField{NoCreate} {}
DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(const Vector2i& size, const Vector2i& processedSize, UnsignedInt radius): GlyphCacheGL{Containers::pointer<State>(size, processedSize, radius)} {}
DistanceFieldGlyphCacheGL::DistanceFieldGlyphCacheGL(NoCreateT) noexcept: GlyphCacheGL{NoCreate} {}
#ifdef MAGNUM_BUILD_DEPRECATED
Vector2i DistanceFieldGlyphCacheGL::distanceFieldTextureSize() const {
return processedSize().xy();
}
#endif
GlyphCacheFeatures DistanceFieldGlyphCacheGL::doFeatures() const {
return GlyphCacheFeature::ImageProcessing
@ -87,6 +103,8 @@ GlyphCacheFeatures DistanceFieldGlyphCacheGL::doFeatures() const {
}
void DistanceFieldGlyphCacheGL::doSetImage(const Vector2i& offset, const ImageView2D& image) {
auto& state = static_cast<State&>(*_state);
GL::Texture2D input;
input.setWrapping(GL::SamplerWrapping::ClampToEdge)
/* Use nearest filter to avoid minor rounding errors on ES2 compared to
@ -110,7 +128,7 @@ void DistanceFieldGlyphCacheGL::doSetImage(const Vector2i& offset, const ImageVi
#endif
{
input.setImage(0, GL::textureFormat(image.format()), ImageView2D{image.format(), size().xy(), image.data()});
_distanceField(input, texture(), {{}, size().xy()/ratio}, size().xy());
state.distanceField(input, texture(), {{}, size().xy()/ratio}, size().xy());
#ifdef MAGNUM_TARGET_WEBGL
static_cast<void>(offset);
#endif
@ -150,7 +168,7 @@ void DistanceFieldGlyphCacheGL::doSetImage(const Vector2i& offset, const ImageVi
image.data()};
input.setImage(0, GL::textureFormat(paddedImage.format()), paddedImage);
_distanceField(input, texture(), Range2Di::fromSize(paddedMinRounded/ratio, paddedImage.size()/ratio), paddedImage.size());
state.distanceField(input, texture(), Range2Di::fromSize(paddedMinRounded/ratio, paddedImage.size()/ratio), paddedImage.size());
}
#endif
}

9
src/Magnum/Text/DistanceFieldGlyphCacheGL.h

@ -35,7 +35,6 @@
#ifdef MAGNUM_TARGET_GL
#include "Magnum/Text/GlyphCacheGL.h"
#include "Magnum/TextureTools/DistanceFieldGL.h"
namespace Magnum { namespace Text {
@ -154,9 +153,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCacheGL: public GlyphCacheGL {
* texture.
* @m_deprecated_since_latest Use @ref processedSize() instead.
*/
CORRADE_DEPRECATED("use processedSize() instead") Vector2i distanceFieldTextureSize() const {
return processedSize().xy();
}
CORRADE_DEPRECATED("use processedSize() instead") Vector2i distanceFieldTextureSize() const;
/**
* @brief Set a distance field cache image
@ -171,10 +168,10 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCacheGL: public GlyphCacheGL {
#endif
private:
struct State;
MAGNUM_TEXT_LOCAL GlyphCacheFeatures doFeatures() const override;
MAGNUM_TEXT_LOCAL void doSetImage(const Vector2i& offset, const ImageView2D& image) override;
TextureTools::DistanceFieldGL _distanceField;
};
}}

49
src/Magnum/Text/GlyphCacheGL.cpp

@ -44,17 +44,18 @@
#if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
#include "Magnum/GL/PixelFormat.h"
#endif
#include "Magnum/Text/Implementation/glyphCacheGLState.h"
namespace Magnum { namespace Text {
GlyphCacheGL::GlyphCacheGL(const PixelFormat format, const Vector2i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache{format, size, processedFormat, processedSize, padding} {
GlyphCacheGL::State::State(const PixelFormat format, const Vector2i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache::State{format, {size, 1}, processedFormat, processedSize, padding} {
#ifndef MAGNUM_TARGET_GLES
if(processedFormat == PixelFormat::R8Unorm)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg);
#endif
/* Initialize the texture */
_texture.setWrapping(GL::SamplerWrapping::ClampToEdge)
texture.setWrapping(GL::SamplerWrapping::ClampToEdge)
.setMinificationFilter(GL::SamplerFilter::Linear)
.setMagnificationFilter(GL::SamplerFilter::Linear);
@ -81,14 +82,16 @@ GlyphCacheGL::GlyphCacheGL(const PixelFormat format, const Vector2i& size, const
textureFormat == GL::TextureFormat::SRGB ||
textureFormat == GL::TextureFormat::RGBA ||
textureFormat == GL::TextureFormat::SRGBAlpha)
_texture.setImage(0, textureFormat, ImageView2D{pixelFormat, GL::PixelType::UnsignedByte, processedSize});
texture.setImage(0, textureFormat, ImageView2D{pixelFormat, GL::PixelType::UnsignedByte, processedSize});
else
_texture.setStorage(1, textureFormat, processedSize);
texture.setStorage(1, textureFormat, processedSize);
#else
_texture.setStorage(1, GL::textureFormat(processedFormat), processedSize);
texture.setStorage(1, GL::textureFormat(processedFormat), processedSize);
#endif
}
GlyphCacheGL::GlyphCacheGL(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache{Containers::pointer<State>(format, size, processedFormat, processedSize, padding)} {}
GlyphCacheGL::GlyphCacheGL(const PixelFormat format, const Vector2i& size, const Vector2i& padding): GlyphCacheGL{format, size, format, size, padding} {}
#ifdef MAGNUM_BUILD_DEPRECATED
@ -104,11 +107,19 @@ GlyphCacheGL::GlyphCacheGL(const Vector2i& size, const Vector2i& padding): Glyph
GlyphCacheGL::GlyphCacheGL(const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding): GlyphCacheGL{PixelFormat::R8Unorm, size, PixelFormat::R8Unorm, processedSize, padding} {}
#endif
GlyphCacheGL::GlyphCacheGL(NoCreateT) noexcept: AbstractGlyphCache{NoCreate}, _texture{NoCreate} {}
GlyphCacheGL::GlyphCacheGL(Containers::Pointer<State>&& state) noexcept: AbstractGlyphCache{Utility::move(state)} {}
GlyphCacheGL::GlyphCacheGL(NoCreateT) noexcept: AbstractGlyphCache{NoCreate} {}
GL::Texture2D& GlyphCacheGL::texture() {
return static_cast<State&>(*_state).texture;
}
GlyphCacheFeatures GlyphCacheGL::doFeatures() const { return {}; }
void GlyphCacheGL::doSetImage(const Vector2i& offset, const ImageView2D& image) {
auto& state = static_cast<State&>(*_state);
CORRADE_ASSERT(format() == processedFormat() && size() == processedSize(),
"Text::GlyphCacheGL::flushImage(): subclass expected to provide a doSetImage() implementation to handle different processed format or size", );
@ -126,11 +137,11 @@ void GlyphCacheGL::doSetImage(const Vector2i& offset, const ImageView2D& image)
formatExtra() and everything else from the view afterwards. */
#ifndef MAGNUM_TARGET_WEBGL
if(image.format() == PixelFormat::R8Unorm && GL::Context::current().isExtensionSupported<GL::Extensions::EXT::texture_rg>()) {
_texture.setSubImage(0, {}, ImageView2D{GL::PixelFormat::Red, GL::PixelType::UnsignedByte, size().xy(), image.data()});
state.texture.setSubImage(0, {}, ImageView2D{GL::PixelFormat::Red, GL::PixelType::UnsignedByte, size().xy(), image.data()});
} else
#endif
{
_texture.setSubImage(0, {}, ImageView2D{image.format(), size().xy(), image.data()});
state.texture.setSubImage(0, {}, ImageView2D{image.format(), size().xy(), image.data()});
}
#ifdef MAGNUM_TARGET_WEBGL
static_cast<void>(offset);
@ -146,11 +157,11 @@ void GlyphCacheGL::doSetImage(const Vector2i& offset, const ImageView2D& image)
format is Red instead of Luminance */
#ifdef MAGNUM_TARGET_GLES2
if(image.format() == PixelFormat::R8Unorm && GL::Context::current().isExtensionSupported<GL::Extensions::EXT::texture_rg>()) {
_texture.setSubImage(0, offset, ImageView2D{image.storage(), GL::PixelFormat::Red, GL::PixelType::UnsignedByte, image.size(), image.data()});
state.texture.setSubImage(0, offset, ImageView2D{image.storage(), GL::PixelFormat::Red, GL::PixelType::UnsignedByte, image.size(), image.data()});
} else
#endif
{
_texture.setSubImage(0, offset, image);
state.texture.setSubImage(0, offset, image);
}
}
#endif
@ -172,33 +183,39 @@ void GlyphCacheGL::doSetProcessedImage(const Vector2i& offset, const ImageView2D
}
#endif
texture().setSubImage(0, offset, imageToUse);
static_cast<State&>(*_state).texture.setSubImage(0, offset, imageToUse);
}
#ifndef MAGNUM_TARGET_GLES
Image3D GlyphCacheGL::doProcessedImage() {
Image2D out = _texture.image(0, PixelFormat::R8Unorm);
Image2D out = static_cast<State&>(*_state).texture.image(0, PixelFormat::R8Unorm);
return Image3D{out.format(), {out.size(), 1}, out.release()};
}
#endif
#ifndef MAGNUM_TARGET_GLES2
GlyphCacheArrayGL::GlyphCacheArrayGL(const PixelFormat format, const Vector3i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache{format, size, processedFormat, processedSize, padding} {
GlyphCacheArrayGL::State::State(const PixelFormat format, const Vector3i& size, const PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache::State{format, size, processedFormat, processedSize, padding} {
#ifndef MAGNUM_TARGET_GLES
if(processedFormat == PixelFormat::R8Unorm)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg);
#endif
/* Initialize the texture */
_texture.setWrapping(GL::SamplerWrapping::ClampToEdge)
texture.setWrapping(GL::SamplerWrapping::ClampToEdge)
.setMinificationFilter(GL::SamplerFilter::Linear)
.setMagnificationFilter(GL::SamplerFilter::Linear)
.setStorage(1, GL::textureFormat(processedFormat), {processedSize, size.z()});
}
GlyphCacheArrayGL::GlyphCacheArrayGL(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache{Containers::pointer<State>(format, size, processedFormat, processedSize, padding)} {}
GlyphCacheArrayGL::GlyphCacheArrayGL(const PixelFormat format, const Vector3i& size, const Vector2i& padding): GlyphCacheArrayGL{format, size, format, size.xy(), padding} {}
GlyphCacheArrayGL::GlyphCacheArrayGL(NoCreateT) noexcept: AbstractGlyphCache{NoCreate}, _texture{NoCreate} {}
GlyphCacheArrayGL::GlyphCacheArrayGL(NoCreateT) noexcept: AbstractGlyphCache{NoCreate} {}
GL::Texture2DArray& GlyphCacheArrayGL::texture() {
return static_cast<State&>(*_state).texture;
}
GlyphCacheFeatures GlyphCacheArrayGL::doFeatures() const { return {}; }
@ -206,7 +223,7 @@ void GlyphCacheArrayGL::doSetImage(const Vector3i& offset, const ImageView3D& im
CORRADE_ASSERT(format() == processedFormat() && size() == processedSize(),
"Text::GlyphCacheArrayGL::flushImage(): subclass expected to provide a doSetImage() implementation to handle different processed format or size", );
_texture.setSubImage(0, offset, image);
static_cast<State&>(*_state).texture.setSubImage(0, offset, image);
}
#endif

17
src/Magnum/Text/GlyphCacheGL.h

@ -34,10 +34,8 @@
#include "Magnum/configure.h"
#ifdef MAGNUM_TARGET_GL
#include "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/Math/Vector2.h"
#include "Magnum/GL/GL.h"
#include "Magnum/Text/AbstractGlyphCache.h"
namespace Magnum { namespace Text {
@ -154,7 +152,7 @@ class MAGNUM_TEXT_EXPORT GlyphCacheGL: public AbstractGlyphCache {
explicit GlyphCacheGL(NoCreateT) noexcept;
/** @brief Cache texture */
GL::Texture2D& texture() { return _texture; }
GL::Texture2D& texture();
protected:
/**
@ -188,7 +186,10 @@ class MAGNUM_TEXT_EXPORT GlyphCacheGL: public AbstractGlyphCache {
#else
protected:
#endif
GL::Texture2D _texture;
struct State;
/* Used by DistanceFieldGlyphCacheGL */
explicit GlyphCacheGL(Containers::Pointer<State>&& state) noexcept;
private:
MAGNUM_TEXT_LOCAL GlyphCacheFeatures doFeatures() const override;
@ -286,14 +287,14 @@ class MAGNUM_TEXT_EXPORT GlyphCacheArrayGL: public AbstractGlyphCache {
explicit GlyphCacheArrayGL(NoCreateT) noexcept;
/** @brief Cache texture */
GL::Texture2DArray& texture() { return _texture; }
GL::Texture2DArray& texture();
#ifdef DOXYGEN_GENERATING_OUTPUT
private:
#else
protected:
#endif
GL::Texture2DArray _texture;
struct State;
private:
MAGNUM_TEXT_LOCAL GlyphCacheFeatures doFeatures() const override;

91
src/Magnum/Text/Implementation/abstractGlyphCacheState.h

@ -0,0 +1,91 @@
#ifndef Magnum_Text_Implementation_abstractGlyphCacheState_h
#define Magnum_Text_Implementation_abstractGlyphCacheState_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023, 2024, 2025
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 <Corrade/Containers/Array.h>
#include "Magnum/Image.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Text/AbstractGlyphCache.h"
#include "Magnum/TextureTools/Atlas.h"
namespace Magnum { namespace Text {
struct AbstractGlyphCache::State {
explicit State(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding);
/* GlyphCacheGL, GlyphCacheArrayGL, DistanceFieldGlyphCacheGL derive from
this to avoid another state struct allocation or putting the extra state
as members, inflating header size */
virtual ~State();
Image3D image;
TextureTools::AtlasLandfill atlas;
PixelFormat processedFormat;
Vector2i processedSize;
Vector2i padding;
/* 0/4 bytes free */
/* First element is glyph position relative to a point on the baseline,
second layer in the texture atlas, third a region in the atlas
slice. Index of the item is ID of the glyph in the cache, refered to
from the fontGlyphMapping array. Index 0 is reserved for an invalid
glyph. */
Containers::Array<Containers::Triple<Vector2i, Int, Range2Di>> glyphs;
/* `fontRanges[i]` to `fontRanges[i + 1]` is the range in
`fontGlyphMapping` containing a mapping for glyphs from font `i`,
`fontGlyphMapping[fontRanges[i]] + j` is then mapping from glyph ID `j`
from font `i` to index in the `glyphs` array, or is 0 if given
glyph isn't present in the cache (which then maps to the invalid
glyph). */
struct Font {
UnsignedInt offset;
/* 4 bytes free on 64b, but not so critical I think */
const AbstractFont* pointer;
};
Containers::Array<Font> fonts;
/* With an assumption that majority of font glyphs get put into a cache,
this achieves O(1) mapping from a font ID + font-specific glyph ID pair
to a cache-global glyph ID with far less overhead than a hashmap would,
and much less memory used as well compared to storing a key, value and a
hash for each mapping entry.
Another assumption is that there's no more than 64k glyphs in total,
which makes the mapping save half memory compared to storing 32-bit
ints. 64K glyphs is enough to fill a 4K texture with 16x16 glyphs, which
seems enough for now. It however might get reached at some point in
practice, in which case the type would simply get changed to a 32-bit
one (and the assertion in addGlyph() then removed). */
Containers::Array<UnsignedShort> fontGlyphMapping;
};
}}
#endif

53
src/Magnum/Text/Implementation/glyphCacheGLState.h

@ -0,0 +1,53 @@
#ifndef Magnum_Text_Implementation_glyphCacheGLState_h
#define Magnum_Text_Implementation_glyphCacheGLState_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023, 2024, 2025
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 "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/Text/Implementation/abstractGlyphCacheState.h"
namespace Magnum { namespace Text {
struct GlyphCacheGL::State: AbstractGlyphCache::State {
explicit State(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding);
GL::Texture2D texture;
};
#ifndef MAGNUM_TARGET_GLES2
struct GlyphCacheArrayGL::State: AbstractGlyphCache::State {
explicit State(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding);
GL::Texture2DArray texture;
};
#endif
}}
#endif

1
src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp

@ -43,6 +43,7 @@
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/PixelFormat.h"
#include "Magnum/GL/Texture.h"
#include "Magnum/Text/DistanceFieldGlyphCacheGL.h"
#include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/ImageData.h"

4
src/Magnum/Text/Test/GlyphCacheGLTest.cpp

@ -42,6 +42,10 @@
#include "Magnum/GL/PixelFormat.h"
#endif
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/GL/TextureFormat.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Range.h"

4
src/Magnum/Text/Test/RendererGLTest.cpp

@ -45,6 +45,10 @@
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Renderbuffer.h"
#include "Magnum/GL/RenderbufferFormat.h"
#include "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/Shaders/VectorGL.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractShaper.h"

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

@ -38,6 +38,7 @@
#endif
#include "Magnum/Math/Range.h"
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Texture.h"
#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/GlyphCacheGL.h"
#include "Magnum/Trade/AbstractImporter.h"

Loading…
Cancel
Save