Browse Source

Text: add a GlyphCacheArrayGL.

So far the Renderer doesn't work with that, and neither the builtin
Vector shaders are be able use it, but gotta start somewhere.

I wanted to have the texture contents tested on ES, but it turns out
that implementing DebugTools::textureSubImage() for arrays is blocked on
another feature I badly need to finish first. Sigh.
pull/674/head
Vladimír Vondruš 1 year ago
parent
commit
8168a06bab
  1. 15
      doc/snippets/Text-gl.cpp
  2. 4
      src/Magnum/Text/AbstractGlyphCache.h
  3. 28
      src/Magnum/Text/GlyphCacheGL.cpp
  4. 101
      src/Magnum/Text/GlyphCacheGL.h
  5. 321
      src/Magnum/Text/Test/GlyphCacheGLTest.cpp
  6. 21
      src/Magnum/Text/Test/GlyphCacheGL_Test.cpp
  7. 3
      src/Magnum/Text/Text.h

15
doc/snippets/Text-gl.cpp

@ -75,6 +75,21 @@ Text::GlyphCacheGL cache{PixelFormat::R8Unorm, {256, 256}};
/* [AbstractGlyphCache-usage-construct] */
}
#ifndef MAGNUM_TARGET_GLES2
{
PluginManager::Manager<Text::AbstractFont> manager;
/* [GlyphCacheArrayGL-usage] */
Containers::Pointer<Text::AbstractFont> font = DOXYGEN_ELLIPSIS(manager.loadAndInstantiate(""));
Text::GlyphCacheArrayGL cache{PixelFormat::R8Unorm, {256, 256, 8}};
if(!font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789?!:;,. "))
Fatal{} << "Glyph cache too small to fit all characters";
/* [GlyphCacheArrayGL-usage] */
}
#endif
{
/* -Wnonnull in GCC 11+ "helpfully" says "this is null" if I don't initialize
the font pointer. I don't care, I just want you to check compilation errors,

4
src/Magnum/Text/AbstractGlyphCache.h

@ -109,8 +109,8 @@ MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& output, GlyphCacheFeatures value);
A GPU-API-agnostic base for glyph caches, supporting multiple fonts and both 2D
and 2D array textures. Provides a common interface for adding fonts, glyph
properties, uploading glyph data and retrieving glyph properties back, the
@ref GlyphCacheGL and @ref DistanceFieldGlyphCacheGL subclasses then provide
concrete implementations backed with an OpenGL texture.
@ref GlyphCacheGL, @ref GlyphCacheArrayGL and @ref DistanceFieldGlyphCacheGL
subclasses then provide concrete implementations backed with an OpenGL texture.
@section Text-AbstractGlyphCache-usage Basic usage

28
src/Magnum/Text/GlyphCacheGL.cpp

@ -182,4 +182,32 @@ Image3D GlyphCacheGL::doProcessedImage() {
}
#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} {
#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)
.setMinificationFilter(GL::SamplerFilter::Linear)
.setMagnificationFilter(GL::SamplerFilter::Linear)
.setStorage(1, GL::textureFormat(processedFormat), {processedSize, size.z()});
}
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} {}
GlyphCacheFeatures GlyphCacheArrayGL::doFeatures() const { return {}; }
void GlyphCacheArrayGL::doSetImage(const Vector3i& offset, const ImageView3D& image) {
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);
}
#endif
}}

101
src/Magnum/Text/GlyphCacheGL.h

@ -27,7 +27,7 @@
*/
/** @file
* @brief Class @ref Magnum::Text::GlyphCacheGL
* @brief Class @ref Magnum::Text::GlyphCacheGL, @ref Magnum::Text::GlyphCacheArrayGL
* @m_since_latest
*/
@ -35,6 +35,9 @@
#ifdef MAGNUM_TARGET_GL
#include "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/Text/AbstractGlyphCache.h"
namespace Magnum { namespace Text {
@ -47,7 +50,8 @@ Implementation of an @ref AbstractGlyphCache backed by a @ref GL::Texture2D.
See the @ref AbstractGlyphCache class documentation for information about
setting up an instance of this class and filling it with glyphs. See the
@ref DistanceFieldGlyphCacheGL subclass for a variant that adds distance field
processing on top.
processing on top, @ref GlyphCacheArrayGL is then using a @ref GL::Texture2DArray
instead.
@section Text-GlyphCacheGL-internal-format Internal texture format
@ -198,6 +202,99 @@ class MAGNUM_TEXT_EXPORT GlyphCacheGL: public AbstractGlyphCache {
#endif
};
#ifndef MAGNUM_TARGET_GLES2
/**
@brief OpenGL array glyph cache
@m_since_latest
Implementation of an @ref AbstractGlyphCache backed by a
@ref GL::Texture2DArray, other than that equivalent to @ref GlyphCacheGL. See
the @ref AbstractGlyphCache class documentation for information about setting
up a glyph cache instance and filling it with glyphs, and @ref GlyphCacheGL for
details on how the internal texture format is picked. The usage differs from
@ref GlyphCacheGL only in specifying one extra dimension for size:
@snippet Text-gl.cpp GlyphCacheArrayGL-usage
@requires_gl30 Extension @gl_extension{EXT,texture_array}
@requires_gles30 Texture arrays are not available in OpenGL ES 2.0.
@requires_webgl20 Texture arrays are not available in WebGL 1.0.
@note This class is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
for more information.
*/
class MAGNUM_TEXT_EXPORT GlyphCacheArrayGL: public AbstractGlyphCache {
public:
/**
* @brief Constructor
* @param format Source image format
* @param size Source image size size in pixels
* @param padding Padding around every glyph in pixels
* @m_since_latest
*
* The @p size is expected to be non-zero. If the implementation
* advertises @ref GlyphCacheFeature::ImageProcessing, the
* @ref processedFormat() and @ref processedSize() is the same as
* @p format and @p size, use @ref AbstractGlyphCache(PixelFormat, const Vector3i&, PixelFormat, const Vector2i&, const Vector2i&)
* to specify different values.
*/
explicit GlyphCacheArrayGL(PixelFormat format, const Vector3i& size, const Vector2i& padding = Vector2i{1});
/**
* @brief Construct with a specific processed format and size
* @param format Source image format
* @param size Source image size size in pixels
* @param processedFormat Processed image format
* @param processedSize Processed glyph cache texture size in
* pixels
* @param padding Padding around every glyph in pixels. See
* @ref Text-AbstractGlyphCache-padding for more information about
* the default.
* @m_since_latest
*
* The @p size and @p processedSize is expected to be non-zero, depth
* of processed size is implicitly the same as in @p size. All glyphs
* are saved in @p format relative to @p size and with @p padding,
* although the actual glyph cache texture is in @p processedFormat and
* has @p processedSize.
* @see @ref AbstractGlyphCache(PixelFormat, const Vector2i&, const Vector2i&)
*/
explicit GlyphCacheArrayGL(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding = Vector2i{1});
/**
* @brief Construct without creating the internal state and the OpenGL texture object
* @m_since_latest
*
* The constructed instance is equivalent to moved-from state, i.e. no
* APIs can be safely called on the object. Useful in cases where you
* will overwrite the instance later anyway. Move another object over
* it to make it useful.
*
* This function can be safely used for constructing (and later
* destructing) objects even without any OpenGL context being active.
* However note that this is a low-level and a potentially dangerous
* API, see the documentation of @ref NoCreate for alternatives.
*/
explicit GlyphCacheArrayGL(NoCreateT) noexcept;
/** @brief Cache texture */
GL::Texture2DArray& texture() { return _texture; }
#ifdef DOXYGEN_GENERATING_OUTPUT
private:
#else
protected:
#endif
GL::Texture2DArray _texture;
private:
MAGNUM_TEXT_LOCAL GlyphCacheFeatures doFeatures() const override;
/* These are not MAGNUM_TEXT_LOCAL because the test makes a subclass */
void doSetImage(const Vector3i& offset, const ImageView3D& image) override;
};
#endif
}}
#else
#error this header is available only in the OpenGL build

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

@ -53,9 +53,21 @@ struct GlyphCacheGLTest: GL::OpenGLTester {
explicit GlyphCacheGLTest();
void construct();
#ifndef MAGNUM_TARGET_GLES2
void constructArray();
#endif
void constructNoPadding();
#ifndef MAGNUM_TARGET_GLES2
void constructNoPaddingArray();
#endif
void constructProcessed();
#ifndef MAGNUM_TARGET_GLES2
void constructProcessedArray();
#endif
void constructProcessedNoPadding();
#ifndef MAGNUM_TARGET_GLES2
void constructProcessedNoPaddingArray();
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
void constructDeprecated();
void constructDeprecatedProcessed();
@ -64,19 +76,45 @@ struct GlyphCacheGLTest: GL::OpenGLTester {
#endif
void constructCopy();
#ifndef MAGNUM_TARGET_GLES2
void constructCopyArray();
#endif
void constructMove();
#ifndef MAGNUM_TARGET_GLES2
void constructMoveArray();
#endif
void setImage();
#ifndef MAGNUM_TARGET_GLES2
void setImageArray();
#endif
void setImageFourChannel();
/* setImageArray() tests a two-channel format, so no need for
setImageFourChannelArray() */
void flushImageSubclassProcessedFormatSize();
#ifndef MAGNUM_TARGET_GLES2
void flushImageSubclassProcessedFormatSizeArray();
#endif
};
GlyphCacheGLTest::GlyphCacheGLTest() {
addTests({&GlyphCacheGLTest::construct,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructArray,
#endif
&GlyphCacheGLTest::constructNoPadding,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructNoPaddingArray,
#endif
&GlyphCacheGLTest::constructProcessed,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructProcessedArray,
#endif
&GlyphCacheGLTest::constructProcessedNoPadding,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructProcessedNoPaddingArray,
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
&GlyphCacheGLTest::constructDeprecated,
&GlyphCacheGLTest::constructDeprecatedProcessed,
@ -85,12 +123,25 @@ GlyphCacheGLTest::GlyphCacheGLTest() {
#endif
&GlyphCacheGLTest::constructCopy,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructCopyArray,
#endif
&GlyphCacheGLTest::constructMove,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructMoveArray,
#endif
&GlyphCacheGLTest::setImage,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::setImageArray,
#endif
&GlyphCacheGLTest::setImageFourChannel,
&GlyphCacheGLTest::flushImageSubclassProcessedFormatSize});
&GlyphCacheGLTest::flushImageSubclassProcessedFormatSize,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::flushImageSubclassProcessedFormatSizeArray,
#endif
});
}
void GlyphCacheGLTest::construct() {
@ -105,6 +156,20 @@ void GlyphCacheGLTest::construct() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructArray() {
GlyphCacheArrayGL cache{PixelFormat::R8Unorm, {1024, 2048, 7}, {3, 2}};
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm);
CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 7}));
CORRADE_COMPARE(cache.padding(), (Vector2i{3, 2}));
#ifndef MAGNUM_TARGET_GLES
CORRADE_COMPARE(cache.texture().imageSize(0), (Vector3i{1024, 2048, 7}));
#endif
}
#endif
void GlyphCacheGLTest::constructNoPadding() {
GlyphCacheGL cache{PixelFormat::RGBA8Unorm, {1024, 2048}};
MAGNUM_VERIFY_NO_GL_ERROR();
@ -117,6 +182,20 @@ void GlyphCacheGLTest::constructNoPadding() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructNoPaddingArray() {
GlyphCacheArrayGL cache{PixelFormat::RGBA8Unorm, {1024, 2048, 7}};
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE(cache.format(), PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 7}));
CORRADE_COMPARE(cache.padding(), Vector2i{1});
#ifndef MAGNUM_TARGET_GLES
CORRADE_COMPARE(cache.texture().imageSize(0), (Vector3i{1024, 2048, 7}));
#endif
}
#endif
void GlyphCacheGLTest::constructProcessed() {
struct Cache: GlyphCacheGL {
explicit Cache(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): GlyphCacheGL{format, size, processedFormat, processedSize, padding} {}
@ -137,6 +216,28 @@ void GlyphCacheGLTest::constructProcessed() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructProcessedArray() {
struct Cache: GlyphCacheArrayGL {
explicit Cache(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): GlyphCacheArrayGL{format, size, processedFormat, processedSize, padding} {}
GlyphCacheFeatures doFeatures() const override {
return GlyphCacheFeature::ImageProcessing;
}
} cache{PixelFormat::R8Unorm, {1024, 2048, 7}, PixelFormat::RGBA8Unorm, {128, 256}, {3, 2}};
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm);
CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 7}));
CORRADE_COMPARE(cache.processedFormat(), PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(cache.processedSize(), (Vector3i{128, 256, 7}));
CORRADE_COMPARE(cache.padding(), (Vector2i{3, 2}));
#ifndef MAGNUM_TARGET_GLES
CORRADE_COMPARE(cache.texture().imageSize(0), (Vector3i{128, 256, 7}));
#endif
}
#endif
void GlyphCacheGLTest::constructProcessedNoPadding() {
struct Cache: GlyphCacheGL {
explicit Cache(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize): GlyphCacheGL{format, size, processedFormat, processedSize} {}
@ -157,6 +258,28 @@ void GlyphCacheGLTest::constructProcessedNoPadding() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructProcessedNoPaddingArray() {
struct Cache: GlyphCacheArrayGL {
explicit Cache(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize): GlyphCacheArrayGL{format, size, processedFormat, processedSize} {}
GlyphCacheFeatures doFeatures() const override {
return GlyphCacheFeature::ImageProcessing;
}
} cache{PixelFormat::R8Unorm, {1024, 2048, 7}, PixelFormat::RGBA8Unorm, {128, 256}};
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm);
CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 7}));
CORRADE_COMPARE(cache.processedFormat(), PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(cache.processedSize(), (Vector3i{128, 256, 7}));
CORRADE_COMPARE(cache.padding(), Vector2i{1});
#ifndef MAGNUM_TARGET_GLES
CORRADE_COMPARE(cache.texture().imageSize(0), (Vector3i{128, 256, 7}));
#endif
}
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
void GlyphCacheGLTest::constructDeprecated() {
CORRADE_IGNORE_DEPRECATED_PUSH
@ -224,6 +347,13 @@ void GlyphCacheGLTest::constructCopy() {
CORRADE_VERIFY(!std::is_copy_assignable<GlyphCacheGL>{});
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructCopyArray() {
CORRADE_VERIFY(!std::is_copy_constructible<GlyphCacheArrayGL>{});
CORRADE_VERIFY(!std::is_copy_assignable<GlyphCacheArrayGL>{});
}
#endif
void GlyphCacheGLTest::constructMove() {
GlyphCacheGL a{PixelFormat::R8Unorm, {1024, 512}};
@ -240,6 +370,24 @@ void GlyphCacheGLTest::constructMove() {
CORRADE_VERIFY(std::is_nothrow_move_assignable<GlyphCacheGL>::value);
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::constructMoveArray() {
GlyphCacheArrayGL a{PixelFormat::R8Unorm, {1024, 512, 7}};
GlyphCacheArrayGL b = Utility::move(a);
CORRADE_COMPARE(b.format(), PixelFormat::R8Unorm);
CORRADE_COMPARE(b.size(), (Vector3i{1024, 512, 7}));
GlyphCacheArrayGL c{PixelFormat::RGBA8Unorm, {2, 3, 3}};
c = Utility::move(b);
CORRADE_COMPARE(c.format(), PixelFormat::R8Unorm);
CORRADE_COMPARE(c.size(), (Vector3i{1024, 512, 7}));
CORRADE_VERIFY(std::is_nothrow_move_constructible<GlyphCacheArrayGL>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<GlyphCacheArrayGL>::value);
}
#endif
const UnsignedByte InputData[]{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
@ -328,6 +476,152 @@ void GlyphCacheGLTest::setImage() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::setImageArray() {
GlyphCacheArrayGL cache{PixelFormat::RG8Unorm, {8, 8, 4}};
/* Fill the texture with non-zero data to verify the padding gets uploaded
as well */
cache.texture().setSubImage(0, {}, Image3D{PixelFormat::RG8Unorm, {8, 8, 4}, Containers::Array<char>{DirectInit, 8*8*4*2, '\xcd'}});
MAGNUM_VERIFY_NO_GL_ERROR();
Utility::copy(
Containers::arrayCast<3, const Vector2ub>(Containers::StridedArrayView4D<const UnsignedByte>{InputData, {2, 2, 4, 2}}),
cache.image().pixels<Vector2ub>().sliceSize({1, 4, 3}, {2, 2, 4}));
cache.flushImage(Range3Di::fromSize({3, 4, 1}, {4, 2, 2}));
MAGNUM_VERIFY_NO_GL_ERROR();
ImageView3D actual3D = cache.image();
MAGNUM_VERIFY_NO_GL_ERROR();
/* The CPU-side image is zero-initialized, what was set above in the
texture isn't present there */
const UnsignedByte expectedData03[8*8*2]{};
const UnsignedByte expectedData1[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0, 0,
0, 0, 0, 0, 0, 0, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
const UnsignedByte expectedData2[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0x00, 0xff, 0x11, 0xee, 0x22, 0xdd, 0x33, 0xcc, 0, 0,
0, 0, 0, 0, 0, 0, 0x44, 0xbb, 0x55, 0xaa, 0x66, 0x99, 0x77, 0x88, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/** @todo ugh have slicing on images directly already, and 3D image
comparison */
const std::size_t sliceSize = actual3D.size().xy().product()*2;
CORRADE_COMPARE_AS((ImageView2D{actual3D.format(), actual3D.size().xy(), actual3D.data()}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedData03}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{actual3D.format(), actual3D.size().xy(), actual3D.data().exceptPrefix(1*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedData1}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{actual3D.format(), actual3D.size().xy(), actual3D.data().exceptPrefix(2*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedData2}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{actual3D.format(), actual3D.size().xy(), actual3D.data().exceptPrefix(3*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedData03}),
DebugTools::CompareImage);
/* The actual texture has just the slice updated, the rest stays. On GLES
we cannot really verify that the size matches, but at least
something. */
#ifdef MAGNUM_TARGET_GLES
/** @todo implement; blocked on Image being able to allocate and slice
itself so I don't need to implement & test all that from scratch just
for that one utility */
CORRADE_SKIP("Cannot verify texture contents because DebugTools::textureSubImage() isn't implemented for texture arrays yet");
#else
Image3D image = cache.texture().image(0, {PixelFormat::RG8Unorm});
MAGNUM_VERIFY_NO_GL_ERROR();
const UnsignedByte expectedTextureData03[]{
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
};
const UnsignedByte expectedTextureData1[]{
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0x00, 0x11,
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0x88, 0x99,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
};
const UnsignedByte expectedTextureData2[]{
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0x00, 0xff,
0x11, 0xee, 0x22, 0xdd, 0x33, 0xcc, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0x44, 0xbb,
0x55, 0xaa, 0x66, 0x99, 0x77, 0x88, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
};
/** @todo ugh have slicing on images directly already, and 3D image
comparison */
CORRADE_COMPARE_AS((ImageView2D{image.format(), image.size().xy(), image.data()}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedTextureData03}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{image.format(), image.size().xy(), image.data().exceptPrefix(1*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedTextureData1}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{image.format(), image.size().xy(), image.data().exceptPrefix(2*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedTextureData2}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS((ImageView2D{image.format(), image.size().xy(), image.data().exceptPrefix(3*sliceSize)}),
(ImageView2D{PixelFormat::RG8Unorm, {8, 8}, expectedTextureData03}),
DebugTools::CompareImage);
#endif
}
#endif
void GlyphCacheGLTest::setImageFourChannel() {
/* Same as setImage(), but with a four-channel format (so quarter of
width). Needed to be able to read the texture on ES2 to verify the
@ -391,6 +685,31 @@ void GlyphCacheGLTest::flushImageSubclassProcessedFormatSize() {
TestSuite::Compare::String);
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGLTest::flushImageSubclassProcessedFormatSizeArray() {
CORRADE_SKIP_IF_NO_ASSERT();
struct Cache: GlyphCacheArrayGL {
explicit Cache(PixelFormat format, const Vector3i& size, PixelFormat processedFormat, const Vector2i& processedSize): GlyphCacheArrayGL{format, size, processedFormat, processedSize} {}
GlyphCacheFeatures doFeatures() const override {
return GlyphCacheFeature::ImageProcessing;
}
};
Cache differentFormat{PixelFormat::R8Unorm, {32, 32, 7}, PixelFormat::RGBA8Unorm, {32, 32}};
Cache differentSize{PixelFormat::R8Unorm, {32, 32, 7}, PixelFormat::R8Unorm, {16, 32}};
Containers::String out;
Error redirectError{&out};
differentFormat.flushImage({{}, {32, 32, 3}});
differentSize.flushImage({{}, {32, 32, 3}});
CORRADE_COMPARE_AS(out,
"Text::GlyphCacheArrayGL::flushImage(): subclass expected to provide a doSetImage() implementation to handle different processed format or size\n"
"Text::GlyphCacheArrayGL::flushImage(): subclass expected to provide a doSetImage() implementation to handle different processed format or size\n",
TestSuite::Compare::String);
}
#endif
}}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::GlyphCacheGLTest)

21
src/Magnum/Text/Test/GlyphCacheGL_Test.cpp

@ -34,10 +34,17 @@ struct GlyphCacheGL_Test: TestSuite::Tester {
explicit GlyphCacheGL_Test();
void constructNoCreate();
#ifndef MAGNUM_TARGET_GLES2
void constructNoCreateArray();
#endif
};
GlyphCacheGL_Test::GlyphCacheGL_Test() {
addTests({&GlyphCacheGL_Test::constructNoCreate});
addTests({&GlyphCacheGL_Test::constructNoCreate,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGL_Test::constructNoCreateArray
#endif
});
}
void GlyphCacheGL_Test::constructNoCreate() {
@ -50,6 +57,18 @@ void GlyphCacheGL_Test::constructNoCreate() {
CORRADE_VERIFY(!std::is_convertible<NoCreateT, GlyphCacheGL>::value);
}
#ifndef MAGNUM_TARGET_GLES2
void GlyphCacheGL_Test::constructNoCreateArray() {
GlyphCacheArrayGL cache{NoCreate};
/* Shouldn't crash or try to acces GL */
CORRADE_VERIFY(true);
/* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<NoCreateT, GlyphCacheArrayGL>::value);
}
#endif
}}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::GlyphCacheGL_Test)

3
src/Magnum/Text/Text.h

@ -63,6 +63,9 @@ class GlyphCacheGL;
typedef CORRADE_DEPRECATED("use DistanceFieldGlyphCacheGL instead") DistanceFieldGlyphCacheGL DistanceFieldGlyphCache;
typedef CORRADE_DEPRECATED("use GlyphCacheGL instead") GlyphCacheGL GlyphCache;
#endif
#ifndef MAGNUM_TARGET_GLES2
class GlyphCacheArrayGL;
#endif
class AbstractRenderer;
template<UnsignedInt> class BasicRenderer;
typedef BasicRenderer<2> Renderer2D;

Loading…
Cancel
Save