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] */ /* [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 /* -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, 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 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 and 2D array textures. Provides a common interface for adding fonts, glyph
properties, uploading glyph data and retrieving glyph properties back, the properties, uploading glyph data and retrieving glyph properties back, the
@ref GlyphCacheGL and @ref DistanceFieldGlyphCacheGL subclasses then provide @ref GlyphCacheGL, @ref GlyphCacheArrayGL and @ref DistanceFieldGlyphCacheGL
concrete implementations backed with an OpenGL texture. subclasses then provide concrete implementations backed with an OpenGL texture.
@section Text-AbstractGlyphCache-usage Basic usage @section Text-AbstractGlyphCache-usage Basic usage

28
src/Magnum/Text/GlyphCacheGL.cpp

@ -182,4 +182,32 @@ Image3D GlyphCacheGL::doProcessedImage() {
} }
#endif #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 /** @file
* @brief Class @ref Magnum::Text::GlyphCacheGL * @brief Class @ref Magnum::Text::GlyphCacheGL, @ref Magnum::Text::GlyphCacheArrayGL
* @m_since_latest * @m_since_latest
*/ */
@ -35,6 +35,9 @@
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
#include "Magnum/GL/Texture.h" #include "Magnum/GL/Texture.h"
#ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h"
#endif
#include "Magnum/Text/AbstractGlyphCache.h" #include "Magnum/Text/AbstractGlyphCache.h"
namespace Magnum { namespace Text { 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 See the @ref AbstractGlyphCache class documentation for information about
setting up an instance of this class and filling it with glyphs. See the setting up an instance of this class and filling it with glyphs. See the
@ref DistanceFieldGlyphCacheGL subclass for a variant that adds distance field @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 @section Text-GlyphCacheGL-internal-format Internal texture format
@ -198,6 +202,99 @@ class MAGNUM_TEXT_EXPORT GlyphCacheGL: public AbstractGlyphCache {
#endif #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 #else
#error this header is available only in the OpenGL build #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(); explicit GlyphCacheGLTest();
void construct(); void construct();
#ifndef MAGNUM_TARGET_GLES2
void constructArray();
#endif
void constructNoPadding(); void constructNoPadding();
#ifndef MAGNUM_TARGET_GLES2
void constructNoPaddingArray();
#endif
void constructProcessed(); void constructProcessed();
#ifndef MAGNUM_TARGET_GLES2
void constructProcessedArray();
#endif
void constructProcessedNoPadding(); void constructProcessedNoPadding();
#ifndef MAGNUM_TARGET_GLES2
void constructProcessedNoPaddingArray();
#endif
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
void constructDeprecated(); void constructDeprecated();
void constructDeprecatedProcessed(); void constructDeprecatedProcessed();
@ -64,19 +76,45 @@ struct GlyphCacheGLTest: GL::OpenGLTester {
#endif #endif
void constructCopy(); void constructCopy();
#ifndef MAGNUM_TARGET_GLES2
void constructCopyArray();
#endif
void constructMove(); void constructMove();
#ifndef MAGNUM_TARGET_GLES2
void constructMoveArray();
#endif
void setImage(); void setImage();
#ifndef MAGNUM_TARGET_GLES2
void setImageArray();
#endif
void setImageFourChannel(); void setImageFourChannel();
/* setImageArray() tests a two-channel format, so no need for
setImageFourChannelArray() */
void flushImageSubclassProcessedFormatSize(); void flushImageSubclassProcessedFormatSize();
#ifndef MAGNUM_TARGET_GLES2
void flushImageSubclassProcessedFormatSizeArray();
#endif
}; };
GlyphCacheGLTest::GlyphCacheGLTest() { GlyphCacheGLTest::GlyphCacheGLTest() {
addTests({&GlyphCacheGLTest::construct, addTests({&GlyphCacheGLTest::construct,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructArray,
#endif
&GlyphCacheGLTest::constructNoPadding, &GlyphCacheGLTest::constructNoPadding,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructNoPaddingArray,
#endif
&GlyphCacheGLTest::constructProcessed, &GlyphCacheGLTest::constructProcessed,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructProcessedArray,
#endif
&GlyphCacheGLTest::constructProcessedNoPadding, &GlyphCacheGLTest::constructProcessedNoPadding,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructProcessedNoPaddingArray,
#endif
#ifdef MAGNUM_BUILD_DEPRECATED #ifdef MAGNUM_BUILD_DEPRECATED
&GlyphCacheGLTest::constructDeprecated, &GlyphCacheGLTest::constructDeprecated,
&GlyphCacheGLTest::constructDeprecatedProcessed, &GlyphCacheGLTest::constructDeprecatedProcessed,
@ -85,12 +123,25 @@ GlyphCacheGLTest::GlyphCacheGLTest() {
#endif #endif
&GlyphCacheGLTest::constructCopy, &GlyphCacheGLTest::constructCopy,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructCopyArray,
#endif
&GlyphCacheGLTest::constructMove, &GlyphCacheGLTest::constructMove,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::constructMoveArray,
#endif
&GlyphCacheGLTest::setImage, &GlyphCacheGLTest::setImage,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::setImageArray,
#endif
&GlyphCacheGLTest::setImageFourChannel, &GlyphCacheGLTest::setImageFourChannel,
&GlyphCacheGLTest::flushImageSubclassProcessedFormatSize}); &GlyphCacheGLTest::flushImageSubclassProcessedFormatSize,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGLTest::flushImageSubclassProcessedFormatSizeArray,
#endif
});
} }
void GlyphCacheGLTest::construct() { void GlyphCacheGLTest::construct() {
@ -105,6 +156,20 @@ void GlyphCacheGLTest::construct() {
#endif #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() { void GlyphCacheGLTest::constructNoPadding() {
GlyphCacheGL cache{PixelFormat::RGBA8Unorm, {1024, 2048}}; GlyphCacheGL cache{PixelFormat::RGBA8Unorm, {1024, 2048}};
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
@ -117,6 +182,20 @@ void GlyphCacheGLTest::constructNoPadding() {
#endif #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() { void GlyphCacheGLTest::constructProcessed() {
struct Cache: GlyphCacheGL { struct Cache: GlyphCacheGL {
explicit Cache(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding): GlyphCacheGL{format, size, processedFormat, processedSize, padding} {} 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 #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() { void GlyphCacheGLTest::constructProcessedNoPadding() {
struct Cache: GlyphCacheGL { struct Cache: GlyphCacheGL {
explicit Cache(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize): GlyphCacheGL{format, size, processedFormat, processedSize} {} 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 #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 #ifdef MAGNUM_BUILD_DEPRECATED
void GlyphCacheGLTest::constructDeprecated() { void GlyphCacheGLTest::constructDeprecated() {
CORRADE_IGNORE_DEPRECATED_PUSH CORRADE_IGNORE_DEPRECATED_PUSH
@ -224,6 +347,13 @@ void GlyphCacheGLTest::constructCopy() {
CORRADE_VERIFY(!std::is_copy_assignable<GlyphCacheGL>{}); 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() { void GlyphCacheGLTest::constructMove() {
GlyphCacheGL a{PixelFormat::R8Unorm, {1024, 512}}; GlyphCacheGL a{PixelFormat::R8Unorm, {1024, 512}};
@ -240,6 +370,24 @@ void GlyphCacheGLTest::constructMove() {
CORRADE_VERIFY(std::is_nothrow_move_assignable<GlyphCacheGL>::value); 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[]{ const UnsignedByte InputData[]{
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
@ -328,6 +476,152 @@ void GlyphCacheGLTest::setImage() {
#endif #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() { void GlyphCacheGLTest::setImageFourChannel() {
/* Same as setImage(), but with a four-channel format (so quarter of /* 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 width). Needed to be able to read the texture on ES2 to verify the
@ -391,6 +685,31 @@ void GlyphCacheGLTest::flushImageSubclassProcessedFormatSize() {
TestSuite::Compare::String); 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) 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(); explicit GlyphCacheGL_Test();
void constructNoCreate(); void constructNoCreate();
#ifndef MAGNUM_TARGET_GLES2
void constructNoCreateArray();
#endif
}; };
GlyphCacheGL_Test::GlyphCacheGL_Test() { GlyphCacheGL_Test::GlyphCacheGL_Test() {
addTests({&GlyphCacheGL_Test::constructNoCreate}); addTests({&GlyphCacheGL_Test::constructNoCreate,
#ifndef MAGNUM_TARGET_GLES2
&GlyphCacheGL_Test::constructNoCreateArray
#endif
});
} }
void GlyphCacheGL_Test::constructNoCreate() { void GlyphCacheGL_Test::constructNoCreate() {
@ -50,6 +57,18 @@ void GlyphCacheGL_Test::constructNoCreate() {
CORRADE_VERIFY(!std::is_convertible<NoCreateT, GlyphCacheGL>::value); 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) 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 DistanceFieldGlyphCacheGL instead") DistanceFieldGlyphCacheGL DistanceFieldGlyphCache;
typedef CORRADE_DEPRECATED("use GlyphCacheGL instead") GlyphCacheGL GlyphCache; typedef CORRADE_DEPRECATED("use GlyphCacheGL instead") GlyphCacheGL GlyphCache;
#endif #endif
#ifndef MAGNUM_TARGET_GLES2
class GlyphCacheArrayGL;
#endif
class AbstractRenderer; class AbstractRenderer;
template<UnsignedInt> class BasicRenderer; template<UnsignedInt> class BasicRenderer;
typedef BasicRenderer<2> Renderer2D; typedef BasicRenderer<2> Renderer2D;

Loading…
Cancel
Save