diff --git a/doc/changelog.dox b/doc/changelog.dox index 1da7d924b..325508549 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1414,6 +1414,11 @@ See also: @cpp setDistanceFieldImage() @ce is deprecated in favor of @ref Text::AbstractGlyphCache::processedSize() and @relativeref{Text::AbstractGlyphCache,setProcessedImage()} + - @ref Text::GlyphCache constructors taking either a + @ref GL::TextureFormat or no texture / pixel format at all are + deprecated in favor of constructors taking an explicit + @ref PixelFormat. The internal texture format is now considered an + implementation detail. - The @cpp TextureTools::atlas() @ce utility is deprecated in favor of @ref TextureTools::AtlasLandfill, which has a vastly better packing efficiency, supports incremental packing and doesn't force the caller to diff --git a/doc/snippets/Text-gl.cpp b/doc/snippets/Text-gl.cpp index a78b2f705..79eda1688 100644 --- a/doc/snippets/Text-gl.cpp +++ b/doc/snippets/Text-gl.cpp @@ -58,7 +58,7 @@ Containers::Pointer font = if(!font->openFile("font.ttf", 12.0f)) Fatal{} << "Can't open font.ttf with StbTrueTypeFont"; -Text::GlyphCache cache{Vector2i{128}}; +Text::GlyphCache cache{PixelFormat::R8Unorm, Vector2i{128}}; font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789?!:;,. "); @@ -67,7 +67,7 @@ font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz" { /* [AbstractGlyphCache-filling-construct] */ -Text::GlyphCache cache{Vector2i{512}}; +Text::GlyphCache cache{PixelFormat::R8Unorm, Vector2i{512}}; /* [AbstractGlyphCache-filling-construct] */ } @@ -96,7 +96,7 @@ PluginManager::Manager manager; Containers::Pointer font = DOXYGEN_ELLIPSIS(manager.loadAndInstantiate("")); font->openFile("font.ttf", 12.0f); -Text::GlyphCache cache{Vector2i{128}}; +Text::GlyphCache cache{PixelFormat::R8Unorm, Vector2i{128}}; font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789?!:;,. "); @@ -116,7 +116,7 @@ Containers::Pointer font = DOXYGEN_ELLIPSIS(manager.loadAndI font->openFile("font.ttf", 12.0f); /* Populate a glyph cache */ -Text::GlyphCache cache{Vector2i{128}}; +Text::GlyphCache cache{PixelFormat::R8Unorm, Vector2i{128}}; font->fillGlyphCache(cache, "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789?!:;,. "); diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.cpp b/src/Magnum/Text/DistanceFieldGlyphCache.cpp index 2f56f2fc4..4c8002b95 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.cpp +++ b/src/Magnum/Text/DistanceFieldGlyphCache.cpp @@ -40,23 +40,23 @@ namespace Magnum { namespace Text { DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& size, const Vector2i& processedSize, const UnsignedInt radius): - #if !(defined(MAGNUM_TARGET_GLES) && defined(MAGNUM_TARGET_GLES2)) - GlyphCache(GL::TextureFormat::R8, size, processedSize, Vector2i(radius)), - #elif !defined(MAGNUM_TARGET_WEBGL) - /* Luminance is not renderable in most cases. RGB is *theoretically* - space-efficient but practically the driver uses RGBA internally anyway, - so just use RGBA. */ - GlyphCache(GL::Context::current().isExtensionSupported() ? - GL::TextureFormat::R8 : GL::TextureFormat::RGBA8, size, processedSize, Vector2i(radius)), - #else - GlyphCache(GL::TextureFormat::RGBA, size, processedSize, Vector2i(radius)), - #endif + GlyphCache{PixelFormat::R8Unorm, size, + #if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_GLES2) + PixelFormat::R8Unorm, + #else + #ifndef MAGNUM_TARGET_WEBGL + /* Without EXT_texture_rg, PixelFormat::R8Unorm maps to Luminance which + is not renderable in most cases. RGB is *theoretically* space- + efficient but practically the driver uses RGBA internally anyway, so + just use RGBA. */ + GL::Context::current().isExtensionSupported() ? + PixelFormat::R8Unorm : + #endif + PixelFormat::RGBA8Unorm, + #endif + processedSize, Vector2i(radius)}, _distanceField{radius} { - #ifndef MAGNUM_TARGET_GLES - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg); - #endif - /* Replicating the assertion from TextureTools::DistanceField so it gets checked during construction already instead of only later during the setImage() call */ @@ -65,7 +65,9 @@ DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& size, const Vec "Text::DistanceFieldGlyphCache: expected source and processed size ratio to be a multiple of 2, got" << Debug::packed << size << "and" << Debug::packed << processedSize, ); #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - /* Luminance is not renderable in most cases */ + /* On ES2 print a warning to make it known that EXT_texture_rg wasn't + available. On WebGL 1 this is the case always, so a warning would be + just a noise. */ if(!GL::Context::current().isExtensionSupported()) Warning() << "Text::DistanceFieldGlyphCache:" << GL::Extensions::EXT::texture_rg::string() << "not supported, using a full RGBA format for the distance field texture"; #endif diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.h b/src/Magnum/Text/DistanceFieldGlyphCache.h index 97df6c56b..bc93c5659 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.h +++ b/src/Magnum/Text/DistanceFieldGlyphCache.h @@ -41,8 +41,8 @@ namespace Magnum { namespace Text { @brief Glyph cache with distance field rendering Unlike the base @ref GlyphCache, this class converts each binary image to a -distance field. It's not possible to use non-binary colors with this cache as -the internal texture format is single-channel. +distance field. It's not possible to only use this cache for monochrome glyphs +as the internal texture format is single-channel. @section Text-DistanceFieldGlyphCache-usage Usage @@ -72,6 +72,23 @@ See the @ref Renderer class for information about text rendering. The @ref AbstractGlyphCache base class has more information about general glyph cache usage. +@section Text-DistanceFieldGlyphCache-internal-format Internal texture format + +The @ref format() is always @ref PixelFormat::R8Unorm. + +On desktop OpenGL, OpenGL ES 3.0+, WebGL 2, and OpenGL ES 2.0 if +@gl_extension{EXT,texture_rg} is supported, the @ref processedFormat() is +always @ref PixelFormat::R8Unorm, which maps to @ref GL::TextureFormat::R8 for +the @ref texture(), matching +@ref Text-GlyphCache-internal-format "the behavior listed in GlyphCache docs". + +On OpenGL ES 2.0 without @gl_extension{EXT,texture_rg} and on WebGL 1, +@ref PixelFormat::R8Unorm maps to @ref GL::TextureFormat::Luminance, which +isn't renderable and thus cannot be used for calculating the distance field. +Instead, @ref PixelFormat::RGBA8Unorm is used for @ref processedFormat(). This +shouldn't affect common use through @ref image(), but code interacting with +@ref processedImage() or @ref setProcessedImage() may need to be aware of this. + @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. diff --git a/src/Magnum/Text/GlyphCache.cpp b/src/Magnum/Text/GlyphCache.cpp index b159f711e..857d04f44 100644 --- a/src/Magnum/Text/GlyphCache.cpp +++ b/src/Magnum/Text/GlyphCache.cpp @@ -25,44 +25,83 @@ #include "GlyphCache.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include +#endif + +#include "Magnum/ImageView.h" +#include "Magnum/GL/TextureFormat.h" +#ifdef MAGNUM_BUILD_DEPRECATED +#include "Magnum/PixelFormat.h" +#endif +#if !defined(MAGNUM_TARGET_GLES) || (defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)) #include "Magnum/PixelFormat.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" -#include "Magnum/GL/TextureFormat.h" - -#ifdef MAGNUM_TARGET_GLES2 -#include "Magnum/ImageView.h" +#endif +#if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#include "Magnum/GL/PixelFormat.h" #endif namespace Magnum { namespace Text { -GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding): GlyphCache{internalFormat, size, size, padding} {} +GlyphCache::GlyphCache(const PixelFormat format, const Vector2i& 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 -/* The unconditional Optional unwrap in here two may assert in rare cases. - Let's hope it doesn't in practice. */ -GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding): AbstractGlyphCache{*GL::genericPixelFormat(internalFormat), size, *GL::genericPixelFormat(internalFormat), processedSize, padding} { /* Initialize the texture */ _texture.setWrapping(GL::SamplerWrapping::ClampToEdge) .setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setStorage(1, internalFormat, processedSize); -} - -GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): GlyphCache{size, size, padding} {} + .setMagnificationFilter(GL::SamplerFilter::Linear); + + /* ES2 special-casing. WebGL 1 has neither EXT_texture_rg nor + EXT_texture_storage so it can use the common code path without + issues. */ + #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* Prefer to use Red instead of Luminance if available, as Luminance isn't + renderable */ + GL::TextureFormat textureFormat = GL::textureFormat(processedFormat); + GL::PixelFormat pixelFormat = GL::pixelFormat(processedFormat); + if(textureFormat == GL::TextureFormat::Luminance && GL::Context::current().isExtensionSupported()) { + textureFormat = GL::TextureFormat::Red; + pixelFormat = GL::PixelFormat::Red; + } -GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding): GlyphCache{ - #ifndef MAGNUM_TARGET_GLES2 - GL::TextureFormat::R8, + /* And use setImage() instead of setStorage() if the format is unsized, as + EXT_texture_storage doesn't allow those */ + if(textureFormat == GL::TextureFormat::Red || + textureFormat == GL::TextureFormat::Luminance || + textureFormat == GL::TextureFormat::RG || + textureFormat == GL::TextureFormat::LuminanceAlpha || + textureFormat == GL::TextureFormat::RGB || + textureFormat == GL::TextureFormat::SRGB || + textureFormat == GL::TextureFormat::RGBA || + textureFormat == GL::TextureFormat::SRGBAlpha) + _texture.setImage(0, textureFormat, ImageView2D{pixelFormat, GL::PixelType::UnsignedByte, processedSize}); + else + _texture.setStorage(1, textureFormat, processedSize); #else - GL::TextureFormat::Luminance, - #endif - size, processedSize, padding} -{ - #ifndef MAGNUM_TARGET_GLES - MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::texture_rg); + _texture.setStorage(1, GL::textureFormat(processedFormat), processedSize); #endif } +GlyphCache::GlyphCache(const PixelFormat format, const Vector2i& size, const Vector2i& padding): GlyphCache{format, size, format, size, padding} {} + +#ifdef MAGNUM_BUILD_DEPRECATED +/* The unconditional Optional unwrap in these two may assert in rare cases. + Let's hope it doesn't in practice. */ + +GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding): GlyphCache{*GL::genericPixelFormat(internalFormat), size, padding} {} + +GlyphCache::GlyphCache(const GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding): GlyphCache{*GL::genericPixelFormat(internalFormat), size, *GL::genericPixelFormat(internalFormat), processedSize, padding} {} + +GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): GlyphCache{PixelFormat::R8Unorm, size, padding} {} + +GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding): GlyphCache{PixelFormat::R8Unorm, size, PixelFormat::R8Unorm, processedSize, padding} {} +#endif + GlyphCache::GlyphCache(NoCreateT) noexcept: AbstractGlyphCache{NoCreate}, _texture{NoCreate} {} GlyphCacheFeatures GlyphCache::doFeatures() const { return {}; } @@ -76,7 +115,18 @@ void GlyphCache::doSetImage(const Vector2i& offset, const ImageView2D& image) { if(!GL::Context::current().isExtensionSupported()) #endif { - _texture.setSubImage(0, {}, ImageView2D{image.format(), size().xy(), image.data()}); + /* On ES2 if EXT_texture_rg is present, the single-channel texture + format is Red instead of Luminance. Have to duplicate the logic here + in addition to below because it's easier than extracting + formatExtra() and everything else from the view afterwards. */ + #ifndef MAGNUM_TARGET_WEBGL + if(image.format() == PixelFormat::R8Unorm && GL::Context::current().isExtensionSupported()) { + _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()}); + } #ifdef MAGNUM_TARGET_WEBGL static_cast(offset); #endif @@ -87,7 +137,16 @@ void GlyphCache::doSetImage(const Vector2i& offset, const ImageView2D& image) { #endif #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) { - _texture.setSubImage(0, offset, image); + /* On ES2 if EXT_texture_rg is present, the single-channel texture + format is Red instead of Luminance */ + #ifdef MAGNUM_TARGET_GLES2 + if(image.format() == PixelFormat::R8Unorm && GL::Context::current().isExtensionSupported()) { + _texture.setSubImage(0, offset, ImageView2D{image.storage(), GL::PixelFormat::Red, GL::PixelType::UnsignedByte, image.size(), image.data()}); + } else + #endif + { + _texture.setSubImage(0, offset, image); + } } #endif } diff --git a/src/Magnum/Text/GlyphCache.h b/src/Magnum/Text/GlyphCache.h index 97f2fd0dc..3bd51c0d9 100644 --- a/src/Magnum/Text/GlyphCache.h +++ b/src/Magnum/Text/GlyphCache.h @@ -53,7 +53,29 @@ See the @ref Renderer class for information about text rendering. The @ref AbstractGlyphCache base class has more information about general glyph cache usage. -@todo Some way for Font to negotiate or check internal texture format +@section Text-GlyphCache-internal-format Internal texture format + +The @ref GL::TextureFormat used by @ref texture() is implicitly coming from +@ref GL::textureFormat(Magnum::PixelFormat) applied to @ref format(), or if +@ref GlyphCacheFeature::ImageProcessing is supported, to @ref processedFormat() +instead. + +If @ref PixelFormat::R8Unorm is used for @ref format() or if +@ref GlyphCacheFeature::ImageProcessing is supported and +@ref PixelFormat::R8Unorm is used for @ref processedFormat(), on desktop OpenGL +the class expects that @gl_extension{ARB,texture_rg} (OpenGL 3.0) is supported +and uses @ref GL::TextureFormat::R8. On OpenGL ES 2.0, if +@gl_extension{EXT,texture_rg} is supported, @ref GL::TextureFormat::Red / +@ref GL::TextureFormat::R8 is used instead of @ref GL::TextureFormat::Luminance +for @ref PixelFormat::R8Unorm. On WebGL 1 @ref GL::TextureFormat::Luminance is +used for @ref PixelFormat::R8Unorm always. + +While this is abstracted away to not affect common use through @ref image(), +@ref processedImage() or @ref setProcessedImage(), code interacting directly +with @ref texture() may need to special-case this. In particular, if image +processing needs to render to the texture, it may need to choose a different +format as luminance usually cannot be rendered to. + @todo Default glyph 0 with rect 0 0 0 0 will result in negative dimensions when nonzero padding is removed @@ -65,50 +87,75 @@ class MAGNUM_TEXT_EXPORT GlyphCache: public AbstractGlyphCache { public: /** * @brief Constructor - * @param internalFormat Internal texture format - * @param size Source glyph cache texture size in pixels - * @param processedSize Processed glyph cache texture size in - * pixels + * @param format Source image format + * @param size Source image size size in pixels * @param padding Padding around every glyph in pixels + * @m_since_latest * - * All glyphs parameters are saved relative to @p size, - * although the actual glyph cache texture has @p processedSize. Glyph - * @p padding can be used to account for e.g. glyph shadows. + * 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 GlyphCache(GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding); + explicit GlyphCache(PixelFormat format, const Vector2i& size, const Vector2i& padding = Vector2i{1}); /** - * @brief Constructor + * @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 * - * Same as calling the above with @p size and @p processedSize being - * set to the same value. See @ref Text-AbstractGlyphCache-padding for - * more information about the default @p padding. + * The @p size and @p processedSize is expected to be non-zero. 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 GlyphCache(GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding = Vector2i{1}); + explicit GlyphCache(PixelFormat format, const Vector2i& size, PixelFormat processedFormat, const Vector2i& processedSize, const Vector2i& padding = Vector2i{1}); + #ifdef MAGNUM_BUILD_DEPRECATED /** * @brief Constructor + * @m_deprecated_since_latest Use @ref GlyphCache(PixelFormat, const Vector2i&, const Vector2i&) + * instead. + */ + CORRADE_DEPRECATED("use GlyphCache(PixelFormat, const Vector2i&, const Vector2i&, const Vector2i&) instead") explicit GlyphCache(GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding = Vector2i{1}); + + /** + * @brief Construct with a specific processed size + * @m_deprecated_since_latest Use @ref GlyphCache(PixelFormat, const Vector2i&, PixelFormat, const Vector2i&, const Vector2i&) + * instead. + */ + CORRADE_DEPRECATED("use GlyphCache(PixelFormat, const Vector2i&, const Vector2i&, const Vector2i&) instead") explicit GlyphCache(GL::TextureFormat internalFormat, const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding); + + /** + * @brief Construct with an implicit format * - * Sets the internal texture format to single-channel. On OpenGL ES - * 3.0+ and WebGL 2 uses @ref GL::TextureFormat::R8. On desktop OpenGL - * requires @gl_extension{ARB,texture_rg} (also part of OpenGL 3.0). On - * ES2 and WebGL 1 unconditionally uses @ref GL::TextureFormat::Luminance. - * This is done for consistency with @ref GL::pixelFormat(), which - * unconditionally returns @ref GL::PixelFormat::Luminance for - * @ref PixelFormat::R8Unorm. See - * @ref GlyphCache(GL::TextureFormat, const Vector2i&, const Vector2i&) - * for an alternative. + * Calls @ref GlyphCache(PixelFormat, const Vector2i&, const Vector2i&) + * with @p format set to @ref PixelFormat::R8Unorm. + * @m_deprecated_since_latest Use @ref GlyphCache(PixelFormat, const Vector2i&, const Vector2i&) + * and explicitly pass the format instead. */ - explicit GlyphCache(const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding); + CORRADE_DEPRECATED("use GlyphCache(PixelFormat, const Vector2i&, const Vector2i&, const Vector2i&) instead") explicit GlyphCache(const Vector2i& size, const Vector2i& padding = Vector2i{1}); /** - * @brief Constructor + * @brief Construct with an implicit format and a specific processed size * - * Same as calling the above with @p size and @p processedSize being - * set to the same value. See @ref Text-AbstractGlyphCache-padding for - * more information about the default @p padding. + * Calls @ref GlyphCache(PixelFormat, const Vector2i&, PixelFormat, const Vector2i&, const Vector2i&) + * with @p format and @p processedFormat set to + * @ref PixelFormat::R8Unorm. + * @m_deprecated_since_latest Use @ref GlyphCache(PixelFormat, const Vector2i&, PixelFormat, const Vector2i&, const Vector2i&) + * and explicitly pass the format instead. */ - explicit GlyphCache(const Vector2i& size, const Vector2i& padding = Vector2i{1}); + CORRADE_DEPRECATED("use GlyphCache(PixelFormat, const Vector2i&, const Vector2i&, const Vector2i&) instead") explicit GlyphCache(const Vector2i& size, const Vector2i& processedSize, const Vector2i& padding); + #endif /** * @brief Construct without creating the internal state and the OpenGL texture object diff --git a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp index cb4799838..262e7a3b1 100644 --- a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp +++ b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp @@ -145,7 +145,26 @@ void DistanceFieldGlyphCacheGLTest::construct() { DistanceFieldGlyphCache cache{{1024, 2048}, {128, 256}, 16}; MAGNUM_VERIFY_NO_GL_ERROR(); + /* The input format is always single-channel */ + CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm); CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + /* The processed format is RGBA if it'd have to be Luminance */ + #ifdef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + if(!GL::Context::current().isExtensionSupported()) + #endif + { + CORRADE_COMPARE(cache.processedFormat(), PixelFormat::RGBA8Unorm); + } + #ifndef MAGNUM_TARGET_WEBGL + else + #endif + #endif + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + { + CORRADE_COMPARE(cache.processedFormat(), PixelFormat::R8Unorm); + } + #endif CORRADE_COMPARE(cache.processedSize(), (Vector3i{128, 256, 1})); #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{128, 256})); diff --git a/src/Magnum/Text/Test/GlyphCacheGLTest.cpp b/src/Magnum/Text/Test/GlyphCacheGLTest.cpp index ed8cca1d2..97480dab9 100644 --- a/src/Magnum/Text/Test/GlyphCacheGLTest.cpp +++ b/src/Magnum/Text/Test/GlyphCacheGLTest.cpp @@ -34,6 +34,11 @@ #ifdef MAGNUM_TARGET_GLES #include "Magnum/DebugTools/TextureImage.h" #endif +#if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#include "Magnum/GL/Context.h" +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/PixelFormat.h" +#endif #include "Magnum/GL/OpenGLTester.h" #include "Magnum/GL/TextureFormat.h" #include "Magnum/Math/Color.h" @@ -46,28 +51,117 @@ struct GlyphCacheGLTest: GL::OpenGLTester { explicit GlyphCacheGLTest(); void construct(); - void constructCustomFormat(); + void constructNoPadding(); + void constructProcessed(); + void constructProcessedNoPadding(); + #ifdef MAGNUM_BUILD_DEPRECATED + void constructDeprecated(); + void constructDeprecatedProcessed(); + void constructDeprecatedTextureFormat(); + void constructDeprecatedTextureFormatProcessed(); + #endif void constructCopy(); void constructMove(); void setImage(); - void setImageCustomFormat(); + void setImageFourChannel(); }; GlyphCacheGLTest::GlyphCacheGLTest() { addTests({&GlyphCacheGLTest::construct, - &GlyphCacheGLTest::constructCustomFormat, + &GlyphCacheGLTest::constructNoPadding, + &GlyphCacheGLTest::constructProcessed, + &GlyphCacheGLTest::constructProcessedNoPadding, + #ifdef MAGNUM_BUILD_DEPRECATED + &GlyphCacheGLTest::constructDeprecated, + &GlyphCacheGLTest::constructDeprecatedProcessed, + &GlyphCacheGLTest::constructDeprecatedTextureFormat, + &GlyphCacheGLTest::constructDeprecatedTextureFormatProcessed, + #endif &GlyphCacheGLTest::constructCopy, &GlyphCacheGLTest::constructMove, &GlyphCacheGLTest::setImage, - &GlyphCacheGLTest::setImageCustomFormat}); + &GlyphCacheGLTest::setImageFourChannel}); } void GlyphCacheGLTest::construct() { + GlyphCache cache{PixelFormat::R8Unorm, {1024, 2048}, {3, 2}}; + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm); + CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + CORRADE_COMPARE(cache.padding(), (Vector2i{3, 2})); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{1024, 2048})); + #endif +} + +void GlyphCacheGLTest::constructNoPadding() { + GlyphCache cache{PixelFormat::RGBA8Unorm, {1024, 2048}}; + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.format(), PixelFormat::RGBA8Unorm); + CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + CORRADE_COMPARE(cache.padding(), Vector2i{1}); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{1024, 2048})); + #endif +} + +void GlyphCacheGLTest::constructProcessed() { + struct : GlyphCache { + using GlyphCache::GlyphCache; + + GlyphCacheFeatures doFeatures() const override { + return GlyphCacheFeature::ImageProcessing; + } + /* The symbol is private, we don't actually need it here, so just + override with an empty implementation */ + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } cache{PixelFormat::R8Unorm, {1024, 2048}, PixelFormat::RGBA8Unorm, {128, 256}, {3, 2}}; + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm); + CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + CORRADE_COMPARE(cache.processedFormat(), PixelFormat::RGBA8Unorm); + CORRADE_COMPARE(cache.processedSize(), (Vector3i{128, 256, 1})); + CORRADE_COMPARE(cache.padding(), (Vector2i{3, 2})); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{128, 256})); + #endif +} + +void GlyphCacheGLTest::constructProcessedNoPadding() { + struct : GlyphCache { + using GlyphCache::GlyphCache; + + GlyphCacheFeatures doFeatures() const override { + return GlyphCacheFeature::ImageProcessing; + } + /* The symbol is private, we don't actually need it here, so just + override with an empty implementation */ + void doSetImage(const Vector2i&, const ImageView2D&) override {} + } cache{PixelFormat::R8Unorm, {1024, 2048}, PixelFormat::RGBA8Unorm, {128, 256}}; + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.format(), PixelFormat::R8Unorm); + CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + CORRADE_COMPARE(cache.processedFormat(), PixelFormat::RGBA8Unorm); + CORRADE_COMPARE(cache.processedSize(), (Vector3i{128, 256, 1})); + CORRADE_COMPARE(cache.padding(), Vector2i{1}); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{128, 256})); + #endif +} + +#ifdef MAGNUM_BUILD_DEPRECATED +void GlyphCacheGLTest::constructDeprecated() { + CORRADE_IGNORE_DEPRECATED_PUSH GlyphCache cache{{1024, 2048}}; + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); @@ -76,7 +170,20 @@ void GlyphCacheGLTest::construct() { #endif } -void GlyphCacheGLTest::constructCustomFormat() { +void GlyphCacheGLTest::constructDeprecatedProcessed() { + CORRADE_IGNORE_DEPRECATED_PUSH + GlyphCache cache{{1024, 2048}, {128, 256}, {}}; + CORRADE_IGNORE_DEPRECATED_POP + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.size(), (Vector3i{1024, 2048, 1})); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{128, 256})); + #endif +} + +void GlyphCacheGLTest::constructDeprecatedTextureFormat() { + CORRADE_IGNORE_DEPRECATED_PUSH GlyphCache cache{ #ifndef MAGNUM_TARGET_GLES2 GL::TextureFormat::RGBA8, @@ -84,6 +191,7 @@ void GlyphCacheGLTest::constructCustomFormat() { GL::TextureFormat::RGBA, #endif {256, 512}}; + CORRADE_IGNORE_DEPRECATED_POP MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE(cache.size(), (Vector3i{256, 512, 1})); @@ -92,19 +200,40 @@ void GlyphCacheGLTest::constructCustomFormat() { #endif } +void GlyphCacheGLTest::constructDeprecatedTextureFormatProcessed() { + CORRADE_IGNORE_DEPRECATED_PUSH + GlyphCache cache{ + #ifndef MAGNUM_TARGET_GLES2 + GL::TextureFormat::RGBA8, + #else + GL::TextureFormat::RGBA, + #endif + {256, 512}, {32, 64}, {}}; + CORRADE_IGNORE_DEPRECATED_POP + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE(cache.size(), (Vector3i{256, 512, 1})); + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(cache.texture().imageSize(0), (Vector2i{32, 64})); + #endif +} +#endif + void GlyphCacheGLTest::constructCopy() { CORRADE_VERIFY(!std::is_copy_constructible{}); CORRADE_VERIFY(!std::is_copy_assignable{}); } void GlyphCacheGLTest::constructMove() { - GlyphCache a{{1024, 512}}; + GlyphCache a{PixelFormat::R8Unorm, {1024, 512}}; GlyphCache b = Utility::move(a); + CORRADE_COMPARE(b.format(), PixelFormat::R8Unorm); CORRADE_COMPARE(b.size(), (Vector3i{1024, 512, 1})); - GlyphCache c{{2, 3}}; + GlyphCache c{PixelFormat::RGBA8Unorm, {2, 3}}; c = Utility::move(b); + CORRADE_COMPARE(c.format(), PixelFormat::R8Unorm); CORRADE_COMPARE(c.size(), (Vector3i{1024, 512, 1})); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); @@ -130,11 +259,19 @@ const UnsignedByte ExpectedData[]{ }; void GlyphCacheGLTest::setImage() { - GlyphCache cache{{16, 8}}; + GlyphCache cache{PixelFormat::R8Unorm, {16, 8}}; /* Fill the texture with non-zero data to verify the padding gets uploaded - as well */ - cache.texture().setSubImage(0, {}, Image2D{PixelFormat::R8Unorm, {16, 8}, Containers::Array{DirectInit, 16*8, '\xcd'}}); + as well. On ES2 with EXT_texture_rg the internal format isn't Luminance + but Red. */ + #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().isExtensionSupported()) + cache.texture().setSubImage(0, {}, Image2D{GL::PixelFormat::Red, GL::PixelType::UnsignedByte, {16, 8}, Containers::Array{DirectInit, 16*8, '\xcd'}}); + else + #endif + { + cache.texture().setSubImage(0, {}, Image2D{PixelFormat::R8Unorm, {16, 8}, Containers::Array{DirectInit, 16*8, '\xcd'}}); + } MAGNUM_VERIFY_NO_GL_ERROR(); Utility::copy( @@ -191,19 +328,13 @@ void GlyphCacheGLTest::setImage() { #endif } -void GlyphCacheGLTest::setImageCustomFormat() { +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 upload works, as there's a special case for when the EXT_unpack_subimage extension isn't present. */ - GlyphCache cache{ - #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) - GL::TextureFormat::RGBA8, - #else - GL::TextureFormat::RGBA, - #endif - {4, 8}}; + GlyphCache cache{PixelFormat::RGBA8Unorm, {4, 8}}; /* Zero the texture to avoid comparing against garbage */ cache.texture().setSubImage(0, {}, Image2D{PixelFormat::RGBA8Unorm, {4, 8}, Containers::Array{ValueInit, 4*4*8}}); diff --git a/src/Magnum/Text/Test/RendererGLTest.cpp b/src/Magnum/Text/Test/RendererGLTest.cpp index ab5bea76a..3a7ffbe56 100644 --- a/src/Magnum/Text/Test/RendererGLTest.cpp +++ b/src/Magnum/Text/Test/RendererGLTest.cpp @@ -30,6 +30,7 @@ #include #include /** @todo drop once Debug is stream-free */ +#include "Magnum/PixelFormat.h" #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/OpenGLTester.h" @@ -114,7 +115,7 @@ struct TestFont: AbstractFont { GlyphCache testGlyphCache(AbstractFont& font) { /* Default padding is 1 to avoid artifacts, set that to 0 to simplify */ - GlyphCache cache{{20, 20}, {}}; + GlyphCache cache{PixelFormat::R8Unorm, {20, 20}, {}}; /* Add one more font to verify the right one gets picked */ cache.addFont(96); diff --git a/src/Magnum/Text/fontconverter.cpp b/src/Magnum/Text/fontconverter.cpp index 56c9a546a..344dfcd1f 100644 --- a/src/Magnum/Text/fontconverter.cpp +++ b/src/Magnum/Text/fontconverter.cpp @@ -29,6 +29,7 @@ #include #include +#include "Magnum/PixelFormat.h" #include "Magnum/Math/ConfigurationValue.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/AbstractFontConverter.h" @@ -211,7 +212,7 @@ int FontConverter::exec() { } else { Debug() << "Zero-size distance field output specified, populating normal glyph cache..."; - cache.emplace(args.value("atlas-size")); + cache.emplace(PixelFormat::R8Unorm, args.value("atlas-size")); } /* Fill the cache */ diff --git a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp index a6ae8af42..8a0840e43 100644 --- a/src/MagnumPlugins/MagnumFont/MagnumFont.cpp +++ b/src/MagnumPlugins/MagnumFont/MagnumFont.cpp @@ -39,6 +39,7 @@ #include #include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" #include "Magnum/Math/ConfigurationValue.h" #include "Magnum/Text/AbstractShaper.h" #include "Magnum/Text/GlyphCache.h" @@ -160,7 +161,9 @@ Vector2 MagnumFont::doGlyphAdvance(const UnsignedInt glyph) { Containers::Pointer MagnumFont::doCreateGlyphCache() { /* Set cache image */ Containers::Pointer cache{InPlaceInit, + PixelFormat::R8Unorm, _opened->conf.value("originalImageSize"), + PixelFormat::R8Unorm, _opened->image->size(), _opened->conf.value("padding")}; /* Copy the opened image data directly to the GL texture because (unlike