From 2f3d7c0482310a35e3a4d25bf2203ac54bcc86d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 30 Sep 2024 22:35:11 +0200 Subject: [PATCH] TextureTools: renamed DistanceField to DistanceFieldGL. Along with the bits in Text library this is one of the last things that still assume OpenGL present by default. As usual, the old name and header is now a deprecated typedef. --- doc/changelog.dox | 22 +- src/Magnum/Shaders/DistanceFieldVectorGL.h | 2 +- src/Magnum/Text/DistanceFieldGlyphCache.cpp | 6 +- src/Magnum/Text/DistanceFieldGlyphCache.h | 6 +- .../Test/DistanceFieldGlyphCacheGLTest.cpp | 2 +- src/Magnum/TextureTools/CMakeLists.txt | 8 +- src/Magnum/TextureTools/DistanceField.h | 169 ++------------- ...{DistanceField.cpp => DistanceFieldGL.cpp} | 24 +-- src/Magnum/TextureTools/DistanceFieldGL.h | 195 ++++++++++++++++++ src/Magnum/TextureTools/Test/CMakeLists.txt | 2 +- .../TextureTools/Test/DistanceFieldGLTest.cpp | 42 ++-- ...FieldTest.cpp => DistanceFieldGL_Test.cpp} | 18 +- .../TextureTools/distancefieldconverter.cpp | 4 +- 13 files changed, 283 insertions(+), 217 deletions(-) rename src/Magnum/TextureTools/{DistanceField.cpp => DistanceFieldGL.cpp} (87%) create mode 100644 src/Magnum/TextureTools/DistanceFieldGL.h rename src/Magnum/TextureTools/Test/{DistanceFieldTest.cpp => DistanceFieldGL_Test.cpp} (79%) diff --git a/doc/changelog.dox b/doc/changelog.dox index 13545fcf8..7619c5c61 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -426,7 +426,7 @@ See also: - New @ref TextureTools::atlasTextureCoordinateTransformation() helper for creating an appropriate texture coordinate transformation matrix for textures placed into an atlas -- Added a @ref TextureTools::DistanceField::operator()() overload taking a +- Added a @ref TextureTools::DistanceFieldGL::operator()() overload taking a @ref GL::Framebuffer instead of a @ref GL::Texture as an output for an easier ability to download the resulting image on OpenGL ES platforms; the @ref magnum-distancefieldconverter "magnum-distancefieldconverter" @@ -862,7 +862,7 @@ See also: @subsubsection changelog-latest-changes-texturetools TextureTools library -- Added a @ref TextureTools::DistanceField::DistanceField(NoCreateT) +- Added a @ref TextureTools::DistanceFieldGL::DistanceFieldGL(NoCreateT) constructor allowing to construct the object without a GL context present @subsubsection changelog-latest-changes-trade Trade library @@ -1183,7 +1183,7 @@ See also: propagate errors when delegating to @ref Text::AbstractFontConverter::exportFontToSingleData() / @ref Text::AbstractFontConverter::exportGlyphCacheToSingleData() -- @ref TextureTools::DistanceField no longer generates an output that's +- @ref TextureTools::DistanceFieldGL no longer generates an output that's slightly shifted, which also makes the output invariant to flips and transpositions - @ref Trade::MeshData::attributeData(UnsignedInt) const was not correctly @@ -1423,6 +1423,10 @@ See also: @ref TextureTools::AtlasLandfill, which has a vastly better packing efficiency, supports incremental packing and doesn't force the caller to use a @ref std::vector +- The @cpp TextureTools::DistanceField @ce class is deprecated in favor of + @ref TextureTools::DistanceFieldGL along with correspondingly renamed + headers, to make room for implementations with other backends such as + Vulkan - @cpp Trade::AbstractMaterialData @ce as well as its containing header `Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new and more flexible @ref Trade::MaterialData. @@ -1806,8 +1810,8 @@ See also: translation like in the original case. Additionally, the `*Integer` variants now only round the axis where division by 2 happened for the middle / center alignment, with the other axis kept unchanged. -- @ref TextureTools::DistanceField now asserts that the output texture has a - framebuffer-drawable format. Before it only printed an error message and +- @ref TextureTools::DistanceFieldGL now asserts that the output texture has + a framebuffer-drawable format. Before it only printed an error message and silently did nothing, causing a hard-to-track error. - The @ref Trade::AnimationTrackTarget enum was extended from 8 to 16 bits to provide more room for custom targets, consistently with @@ -3114,7 +3118,7 @@ Released 2019-10-24, tagged as @subsubsection changelog-2019-10-changes-texturetools TextureTools library -- @ref TextureTools::DistanceField was updated to work on ES3 SwiftShader +- @cpp TextureTools::DistanceField @ce was updated to work on ES3 SwiftShader contexts (which have broken @glsl gl_VertexID @ce) @subsubsection changelog-2019-10-changes-platform Platform libraries @@ -3594,8 +3598,8 @@ Released 2019-02-04, tagged as @subsubsection changelog-2019-01-changes-texturetools TextureTools library - Further performance and output quality improvements for - @ref TextureTools::DistanceField, making the ES2/WebGL 1 consistent with - desktop and speeding up the processing to take only 60% of the time + @cpp TextureTools::DistanceField @ce, making the ES2/WebGL 1 consistent + with desktop and speeding up the processing to take only 60% of the time compared to before. It's now also possible to reuse the internal state for batch processing. @@ -3664,7 +3668,7 @@ Released 2019-02-04, tagged as @ref Corrade/Containers/PointerStl.h header explicitly. - @cpp TextureTools::distanceField() @ce is deprecated due to inefficiency of its statelessness when doing batch processing. Use the - @ref TextureTools::DistanceField class instead. + @cpp TextureTools::DistanceField @ce class instead. - @cpp Platform::GlfwApplication::KeyEvent::Key::Smicolon @ce has a typo and so is deprecated in favor of @ref Platform::GlfwApplication::KeyEvent::Key::Semicolon diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 085394f01..0c9e8cd3f 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -61,7 +61,7 @@ namespace Implementation { @m_since_latest Renders vector graphics in a form of signed distance field. See -@ref TextureTools::DistanceField for more information and @ref VectorGL for a +@ref TextureTools::DistanceFieldGL for more information and @ref VectorGL for a simpler variant of this shader. Note that the final rendered outlook will greatly depend on radius of input distance field and value passed to @ref setSmoothness(). You need to provide @ref Position and diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.cpp b/src/Magnum/Text/DistanceFieldGlyphCache.cpp index 4c8002b95..b052c1981 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.cpp +++ b/src/Magnum/Text/DistanceFieldGlyphCache.cpp @@ -35,7 +35,7 @@ #endif #include "Magnum/GL/TextureFormat.h" #include "Magnum/Math/Range.h" -#include "Magnum/TextureTools/DistanceField.h" +#include "Magnum/TextureTools/DistanceFieldGL.h" namespace Magnum { namespace Text { @@ -57,7 +57,7 @@ DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& size, const Vec processedSize, Vector2i(radius)}, _distanceField{radius} { - /* Replicating the assertion from TextureTools::DistanceField so it gets + /* Replicating the assertion from TextureTools::DistanceFieldGL so it gets checked during construction already instead of only later during the setImage() call */ CORRADE_ASSERT(size % processedSize == Vector2i{0} && @@ -125,7 +125,7 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView const Vector2i paddedMin = image.storage().skip().xy(); const Vector2i paddedMax = image.size() + image.storage().skip().xy(); - /* TextureTools::DistanceField expects the input size and output + /* TextureTools::DistanceFieldGL expects the input size and output rectangle size ratio to be a multiple of 2 in order for the shader to perform pixel addressing correctly. That might not always be the case with the rectangle passed to flushImage(), so round the diff --git a/src/Magnum/Text/DistanceFieldGlyphCache.h b/src/Magnum/Text/DistanceFieldGlyphCache.h index bc93c5659..2462aceb1 100644 --- a/src/Magnum/Text/DistanceFieldGlyphCache.h +++ b/src/Magnum/Text/DistanceFieldGlyphCache.h @@ -33,7 +33,7 @@ #ifdef MAGNUM_TARGET_GL #include "Magnum/Text/GlyphCache.h" -#include "Magnum/TextureTools/DistanceField.h" +#include "Magnum/TextureTools/DistanceFieldGL.h" namespace Magnum { namespace Text { @@ -93,7 +93,7 @@ shouldn't affect common use through @ref image(), but code interacting with @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features for more information. -@see @ref TextureTools::DistanceField +@see @ref TextureTools::DistanceFieldGL */ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { public: @@ -167,7 +167,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { MAGNUM_TEXT_LOCAL Image3D doProcessedImage() override; #endif - TextureTools::DistanceField _distanceField; + TextureTools::DistanceFieldGL _distanceField; }; }} diff --git a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp index 262e7a3b1..c5e9de2ac 100644 --- a/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp +++ b/src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp @@ -177,7 +177,7 @@ void DistanceFieldGlyphCacheGLTest::constructSizeRatioNotMultipleOfTwo() { /* This should be fine */ DistanceFieldGlyphCache{Vector2i{23*14}, Vector2i{23}, 4}; - /* It's the same assert as in TextureTools::DistanceField */ + /* It's the same assert as in TextureTools::DistanceFieldGL */ std::ostringstream out; Error redirectError{&out}; DistanceFieldGlyphCache{Vector2i{23*14}, Vector2i{23*2}, 4}; diff --git a/src/Magnum/TextureTools/CMakeLists.txt b/src/Magnum/TextureTools/CMakeLists.txt index ee25d89fe..d183252e4 100644 --- a/src/Magnum/TextureTools/CMakeLists.txt +++ b/src/Magnum/TextureTools/CMakeLists.txt @@ -60,10 +60,14 @@ if(MAGNUM_TARGET_GL) endif() list(APPEND MagnumTextureTools_GracefulAssert_SRCS - DistanceField.cpp + DistanceFieldGL.cpp ${MagnumTextureTools_RESOURCES}) - list(APPEND MagnumTextureTools_HEADERS DistanceField.h) + list(APPEND MagnumTextureTools_HEADERS DistanceFieldGL.h) + + if(MAGNUM_BUILD_DEPRECATED) + list(APPEND MagnumTextureTools_HEADERS DistanceField.h) + endif() endif() # TextureTools library diff --git a/src/Magnum/TextureTools/DistanceField.h b/src/Magnum/TextureTools/DistanceField.h index 0c273f49a..4f9fb0ddd 100644 --- a/src/Magnum/TextureTools/DistanceField.h +++ b/src/Magnum/TextureTools/DistanceField.h @@ -25,171 +25,34 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Class @ref Magnum::TextureTools::DistanceField + * @brief Typedef @ref Magnum::TextureTools::DistanceField + * @m_deprecated_since_latest Use @ref Magnum/TextureTools/DistanceFieldGL.h + * and the @relativeref{Magnum::TextureTools,DistanceFieldGL} class + * instead. */ - -#include "Magnum/configure.h" - -#ifdef MAGNUM_TARGET_GL -#include - -#include "Magnum/Magnum.h" -#include "Magnum/GL/GL.h" -#ifndef MAGNUM_TARGET_GLES -#include "Magnum/Math/Vector2.h" #endif -#include "Magnum/TextureTools/visibility.h" - -namespace Magnum { namespace TextureTools { - -/** -@brief Create a signed distance field - -Converts a binary black/white image (stored in the red channel of @p input) to -a signed distance field (stored in the red channel of @p output @p rectangle). -The purpose of this function is to convert a high-resolution binary image (such -as vector artwork or font glyphs) to a low-resolution grayscale image. The -image will then occupy much less memory and can be scaled without aliasing -issues. Additionally it provides foundation for features like outlining, glow -or drop shadow essentially for free. - -You can also use the @ref magnum-distancefieldconverter "magnum-distancefieldconverter" -utility to do distance field conversion on command-line. This functionality is -also used inside the @ref magnum-fontconverter "magnum-fontconverter" utility. - -@section TextureTools-DistanceField-algorithm The algorithm - -For each pixel inside the @p output sub-rectangle the algorithm looks at -corresponding pixel in the input and tries to find nearest pixel of opposite -color in an area defined @p radius. Signed distance between the points is then -saved as value of given pixel in @p output. Value of 1.0 means that the pixel -was originally colored white and nearest black pixel is farther than @p radius, -value of 0.0 means that the pixel was originally black and nearest white pixel -is farther than @p radius. Values around 0.5 are around edges. - -The resulting texture can be used with bilinear filtering. It can be converted -back to binary form in shader using e.g. GLSL @glsl smoothstep() @ce function -with step around 0.5 to create antialiased edges. Or you can exploit the -distance field features to create many other effects. See also -@ref Shaders::DistanceFieldVectorGL. -Based on: *Chris Green - Improved Alpha-Tested Magnification for Vector Textures -and Special Effects, SIGGRAPH 2007, -http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf* - -@attention This is a GPU-only implementation, so it expects an active GL - context. - -@note This function 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_TEXTURETOOLS_EXPORT DistanceField { - public: - /** - * @brief Constructor - * @param radius Max lookup radius in the input texture - * - * Prepares the shader and other internal state for given @p radius. - */ - explicit DistanceField(UnsignedInt radius); - - /** - * @brief Construct without creating the internal OpenGL state - * @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 DistanceField(NoCreateT) noexcept; - - /** @brief Copying is not allowed */ - DistanceField(const DistanceField&) = delete; - - /** - * @brief Move constructor - * @m_since_latest - * - * Performs a destructive move, i.e. the original object isn't usable - * afterwards anymore. - */ - DistanceField(DistanceField&&) noexcept; - - ~DistanceField(); - - /** @brief Copying is not allowed */ - DistanceField& operator=(const DistanceField&) = delete; +#include "Magnum/configure.h" - /** @brief Move constructor */ - DistanceField& operator=(DistanceField&&) noexcept; +#ifdef MAGNUM_BUILD_DEPRECATED +#include - /** @brief Max lookup radius */ - UnsignedInt radius() const; +#include "Magnum/TextureTools/DistanceFieldGL.h" - /** - * @brief Calculate the distance field to a framebuffer - * @param input Input texture - * @param output Output framebuffer - * @param rectangle Rectangle in output texture where to render - * @param imageSize Input texture size. Needed only for OpenGL ES, - * on desktop GL the information is gathered automatically using - * @ref GL::Texture2D::imageSize(). - * @m_since_latest - * - * The @p output texture is expected to have a framebuffer-drawable - * @ref GL::TextureFormat. On desktop OpenGL and - * OpenGL ES 3.0 it's common to render to @ref GL::TextureFormat::R8. - * On OpenGL ES 2.0 you can use @ref GL::TextureFormat::Red if - * @gl_extension{EXT,texture_rg} is available; if not, the smallest but - * still inefficient supported format is in most cases - * @ref GL::TextureFormat::RGB. The @ref GL::TextureFormat::Luminance - * format usually isn't renderable. - * - * Additionally, the ratio of the @p input size (or @p imageSize on - * OpenGL ES) and @p rectangle size is expected to be a multiple of 2, - * as that's what the generator shader relies on for correct pixel - * addressing. - */ - void operator()(GL::Texture2D& input, GL::Framebuffer& output, const Range2Di& rectangle, const Vector2i& imageSize - #ifndef MAGNUM_TARGET_GLES - = {} - #endif - ); +CORRADE_DEPRECATED_FILE("use Magnum/TextureTools/DistanceFieldGL.h and the DistanceFieldGL class instead") - /** - * @brief Calculate the distance field to a texture - * @param input Input texture - * @param output Output texture - * @param rectangle Rectangle in output texture where to render - * @param imageSize Input texture size. Needed only for OpenGL ES, - * on desktop GL the information is gathered automatically using - * @ref GL::Texture2D::imageSize(). - * - * Creates a framebuffer with @p output attached and calls - * @ref operator()(GL::Texture2D&, GL::Framebuffer&, const Range2Di&, const Vector2i&). - */ - void operator()(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, const Vector2i& imageSize - #ifndef MAGNUM_TARGET_GLES - = {} - #endif - ); +namespace Magnum { namespace TextureTools { - private: - struct State; - Containers::Pointer _state; -}; +/** @brief @copybrief DistanceFieldGL + * @m_deprecated_since_latest Use @ref DistanceFieldGL instead. + */ +typedef CORRADE_DEPRECATED("use DistanceFieldGL instead") DistanceFieldGL DistanceField; }} #else -#error this header is available only in the OpenGL build +#error use Magnum/TextureTools/DistanceFieldGL.h and the DistanceFieldGL class instead #endif #endif diff --git a/src/Magnum/TextureTools/DistanceField.cpp b/src/Magnum/TextureTools/DistanceFieldGL.cpp similarity index 87% rename from src/Magnum/TextureTools/DistanceField.cpp rename to src/Magnum/TextureTools/DistanceFieldGL.cpp index 4c0dc1c9a..f0caf76c7 100644 --- a/src/Magnum/TextureTools/DistanceField.cpp +++ b/src/Magnum/TextureTools/DistanceFieldGL.cpp @@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE. */ -#include "DistanceField.h" +#include "DistanceFieldGL.h" #include #include @@ -140,7 +140,7 @@ DistanceFieldShader::DistanceFieldShader(const UnsignedInt radius) { } -struct DistanceField::State { +struct DistanceFieldGL::State { explicit State(UnsignedInt radius): shader{radius}, radius{radius} {} DistanceFieldShader shader; @@ -148,7 +148,7 @@ struct DistanceField::State { GL::Mesh mesh; }; -DistanceField::DistanceField(const UnsignedInt radius): _state{new State{radius}} { +DistanceFieldGL::DistanceFieldGL(const UnsignedInt radius): _state{new State{radius}} { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::framebuffer_object); #endif @@ -171,17 +171,17 @@ DistanceField::DistanceField(const UnsignedInt radius): _state{new State{radius} } } -DistanceField::DistanceField(NoCreateT) noexcept {} +DistanceFieldGL::DistanceFieldGL(NoCreateT) noexcept {} -DistanceField::DistanceField(DistanceField&&) noexcept = default; +DistanceFieldGL::DistanceFieldGL(DistanceFieldGL&&) noexcept = default; -DistanceField::~DistanceField() = default; +DistanceFieldGL::~DistanceFieldGL() = default; -DistanceField& DistanceField::operator=(DistanceField&&) noexcept = default; +DistanceFieldGL& DistanceFieldGL::operator=(DistanceFieldGL&&) noexcept = default; -UnsignedInt DistanceField::radius() const { return _state->radius; } +UnsignedInt DistanceFieldGL::radius() const { return _state->radius; } -void DistanceField::operator()(GL::Texture2D& input, GL::Framebuffer& output, const Range2Di& rectangle, const Vector2i& +void DistanceFieldGL::operator()(GL::Texture2D& input, GL::Framebuffer& output, const Range2Di& rectangle, const Vector2i& #ifdef MAGNUM_TARGET_GLES imageSize #endif @@ -193,14 +193,14 @@ void DistanceField::operator()(GL::Texture2D& input, GL::Framebuffer& output, co #endif CORRADE_ASSERT(output.checkStatus(GL::FramebufferTarget::Draw) == GL::Framebuffer::Status::Complete, - "TextureTools::DistanceField: output texture format not framebuffer-drawable:" << output.checkStatus(GL::FramebufferTarget::Draw), ); + "TextureTools::DistanceFieldGL: output texture format not framebuffer-drawable:" << output.checkStatus(GL::FramebufferTarget::Draw), ); /* The shader assumes that the ratio between the output and input is a multiple of 2, causing output pixel *centers* to be aligned with input pixel *edges* */ CORRADE_ASSERT(imageSize % rectangle.size() == Vector2i{0} && (imageSize/rectangle.size()) % 2 == Vector2i{0}, - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got" << Debug::packed << imageSize << "and" << Debug::packed << rectangle.size(), ); + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got" << Debug::packed << imageSize << "and" << Debug::packed << rectangle.size(), ); /* Save existing viewport to restore it back after */ const Range2Di previousViewport = output.viewport(); @@ -218,7 +218,7 @@ void DistanceField::operator()(GL::Texture2D& input, GL::Framebuffer& output, co output.setViewport(previousViewport); } -void DistanceField::operator()(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, const Vector2i& +void DistanceFieldGL::operator()(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, const Vector2i& #ifdef MAGNUM_TARGET_GLES imageSize #endif diff --git a/src/Magnum/TextureTools/DistanceFieldGL.h b/src/Magnum/TextureTools/DistanceFieldGL.h new file mode 100644 index 000000000..86c0bf4c8 --- /dev/null +++ b/src/Magnum/TextureTools/DistanceFieldGL.h @@ -0,0 +1,195 @@ +#ifndef Magnum_TextureTools_DistanceFieldGL_h +#define Magnum_TextureTools_DistanceFieldGL_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022, 2023 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::TextureTools::DistanceFieldGL + */ + +#include "Magnum/configure.h" + +#ifdef MAGNUM_TARGET_GL +#include + +#include "Magnum/Magnum.h" +#include "Magnum/GL/GL.h" +#ifndef MAGNUM_TARGET_GLES +#include "Magnum/Math/Vector2.h" +#endif +#include "Magnum/TextureTools/visibility.h" + +namespace Magnum { namespace TextureTools { + +/** +@brief Create a signed distance field using OpenGL + +Converts a binary black/white image (stored in the red channel of @p input) to +a signed distance field (stored in the red channel of @p output @p rectangle). +The purpose of this function is to convert a high-resolution binary image (such +as vector artwork or font glyphs) to a low-resolution grayscale image. The +image will then occupy much less memory and can be scaled without aliasing +issues. Additionally it provides foundation for features like outlining, glow +or drop shadow essentially for free. + +You can also use the @ref magnum-distancefieldconverter "magnum-distancefieldconverter" +utility to do distance field conversion on command-line. This functionality is +also used inside the @ref magnum-fontconverter "magnum-fontconverter" utility. + +@section TextureTools-DistanceFieldGL-algorithm The algorithm + +For each pixel inside the @p output sub-rectangle the algorithm looks at +corresponding pixel in the input and tries to find nearest pixel of opposite +color in an area defined @p radius. Signed distance between the points is then +saved as value of given pixel in @p output. Value of 1.0 means that the pixel +was originally colored white and nearest black pixel is farther than @p radius, +value of 0.0 means that the pixel was originally black and nearest white pixel +is farther than @p radius. Values around 0.5 are around edges. + +The resulting texture can be used with bilinear filtering. It can be converted +back to binary form in shader using e.g. GLSL @glsl smoothstep() @ce function +with step around 0.5 to create antialiased edges. Or you can exploit the +distance field features to create many other effects. See also +@ref Shaders::DistanceFieldVectorGL. + +Based on: *Chris Green - Improved Alpha-Tested Magnification for Vector Textures +and Special Effects, SIGGRAPH 2007, +http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf* + +@attention This is a GPU-only implementation, so it expects an active GL + context. + +@note This function 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_TEXTURETOOLS_EXPORT DistanceFieldGL { + public: + /** + * @brief Constructor + * @param radius Max lookup radius in the input texture + * + * Prepares the shader and other internal state for given @p radius. + */ + explicit DistanceFieldGL(UnsignedInt radius); + + /** + * @brief Construct without creating the internal OpenGL state + * @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 DistanceFieldGL(NoCreateT) noexcept; + + /** @brief Copying is not allowed */ + DistanceFieldGL(const DistanceFieldGL&) = delete; + + /** + * @brief Move constructor + * @m_since_latest + * + * Performs a destructive move, i.e. the original object isn't usable + * afterwards anymore. + */ + DistanceFieldGL(DistanceFieldGL&&) noexcept; + + ~DistanceFieldGL(); + + /** @brief Copying is not allowed */ + DistanceFieldGL& operator=(const DistanceFieldGL&) = delete; + + /** @brief Move constructor */ + DistanceFieldGL& operator=(DistanceFieldGL&&) noexcept; + + /** @brief Max lookup radius */ + UnsignedInt radius() const; + + /** + * @brief Calculate the distance field to a framebuffer + * @param input Input texture + * @param output Output framebuffer + * @param rectangle Rectangle in output texture where to render + * @param imageSize Input texture size. Needed only for OpenGL ES, + * on desktop GL the information is gathered automatically using + * @ref GL::Texture2D::imageSize(). + * @m_since_latest + * + * The @p output texture is expected to have a framebuffer-drawable + * @ref GL::TextureFormat. On desktop OpenGL and + * OpenGL ES 3.0 it's common to render to @ref GL::TextureFormat::R8. + * On OpenGL ES 2.0 you can use @ref GL::TextureFormat::Red if + * @gl_extension{EXT,texture_rg} is available; if not, the smallest but + * still inefficient supported format is in most cases + * @ref GL::TextureFormat::RGB. The @ref GL::TextureFormat::Luminance + * format usually isn't renderable. + * + * Additionally, the ratio of the @p input size (or @p imageSize on + * OpenGL ES) and @p rectangle size is expected to be a multiple of 2, + * as that's what the generator shader relies on for correct pixel + * addressing. + */ + void operator()(GL::Texture2D& input, GL::Framebuffer& output, const Range2Di& rectangle, const Vector2i& imageSize + #ifndef MAGNUM_TARGET_GLES + = {} + #endif + ); + + /** + * @brief Calculate the distance field to a texture + * @param input Input texture + * @param output Output texture + * @param rectangle Rectangle in output texture where to render + * @param imageSize Input texture size. Needed only for OpenGL ES, + * on desktop GL the information is gathered automatically using + * @ref GL::Texture2D::imageSize(). + * + * Creates a framebuffer with @p output attached and calls + * @ref operator()(GL::Texture2D&, GL::Framebuffer&, const Range2Di&, const Vector2i&). + */ + void operator()(GL::Texture2D& input, GL::Texture2D& output, const Range2Di& rectangle, const Vector2i& imageSize + #ifndef MAGNUM_TARGET_GLES + = {} + #endif + ); + + private: + struct State; + Containers::Pointer _state; +}; + +}} +#else +#error this header is available only in the OpenGL build +#endif + +#endif diff --git a/src/Magnum/TextureTools/Test/CMakeLists.txt b/src/Magnum/TextureTools/Test/CMakeLists.txt index 063f824ad..720f2fff0 100644 --- a/src/Magnum/TextureTools/Test/CMakeLists.txt +++ b/src/Magnum/TextureTools/Test/CMakeLists.txt @@ -88,7 +88,7 @@ else() endif() if(MAGNUM_TARGET_GL) - corrade_add_test(TextureToolsDistanceFieldTest DistanceFieldTest.cpp LIBRARIES MagnumTextureTools) + corrade_add_test(TextureToolsDistanceFieldGL_Test DistanceFieldGL_Test.cpp LIBRARIES MagnumTextureTools) if(MAGNUM_BUILD_GL_TESTS) # Otherwise CMake complains that Corrade::PluginManager is not found, wtf diff --git a/src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp b/src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp index b3b8c4415..583264600 100644 --- a/src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp +++ b/src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp @@ -50,7 +50,7 @@ #include "Magnum/GL/PixelFormat.h" #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" -#include "Magnum/TextureTools/DistanceField.h" +#include "Magnum/TextureTools/DistanceFieldGL.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/ImageData.h" @@ -135,27 +135,27 @@ DistanceFieldGLTest::DistanceFieldGLTest() { } void DistanceFieldGLTest::construct() { - DistanceField distanceField{32}; + DistanceFieldGL distanceField{32}; CORRADE_COMPARE(distanceField.radius(), 32); } void DistanceFieldGLTest::constructCopy() { - CORRADE_VERIFY(!std::is_copy_constructible{}); - CORRADE_VERIFY(!std::is_copy_assignable{}); + CORRADE_VERIFY(!std::is_copy_constructible{}); + CORRADE_VERIFY(!std::is_copy_assignable{}); } void DistanceFieldGLTest::constructMove() { - DistanceField a{16}; + DistanceFieldGL a{16}; - DistanceField b = Utility::move(a); + DistanceFieldGL b = Utility::move(a); CORRADE_COMPARE(b.radius(), 16); - DistanceField c{8}; + DistanceFieldGL c{8}; c = Utility::move(b); CORRADE_COMPARE(c.radius(), 16); - CORRADE_VERIFY(std::is_nothrow_move_constructible::value); - CORRADE_VERIFY(std::is_nothrow_move_assignable::value); + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } void DistanceFieldGLTest::runTexture() { @@ -236,7 +236,7 @@ void DistanceFieldGLTest::runTexture() { output.setSubImage(0, {}, ImageView2D{outputPixelFormat, outputPixelType, data.size, Containers::Array{DirectInit, std::size_t(data.size.product()*GL::pixelFormatSize(outputPixelFormat, outputPixelType)), '\x66'}}); - DistanceField distanceField{32}; + DistanceFieldGL distanceField{32}; CORRADE_COMPARE(distanceField.radius(), 32); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -389,7 +389,7 @@ void DistanceFieldGLTest::runFramebuffer() { GL::Renderer::setClearColor(0x1f1f1f_rgbf); #endif - DistanceField distanceField{32}; + DistanceFieldGL distanceField{32}; CORRADE_COMPARE(distanceField.radius(), 32); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -478,7 +478,7 @@ void DistanceFieldGLTest::formatNotDrawable() { output.setImage(0, GL::TextureFormat::RGB9E5, ImageView2D{GL::PixelFormat::RGB, GL::PixelType::UnsignedInt5999Rev, Vector2i{4}}); #endif - DistanceField distanceField{4}; + DistanceFieldGL distanceField{4}; std::ostringstream out; Error redirectError{&out}; @@ -488,9 +488,9 @@ void DistanceFieldGLTest::formatNotDrawable() { #endif ); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(out.str(), "TextureTools::DistanceField: output texture format not framebuffer-drawable: GL::Framebuffer::Status::Unsupported\n"); + CORRADE_COMPARE(out.str(), "TextureTools::DistanceFieldGL: output texture format not framebuffer-drawable: GL::Framebuffer::Status::Unsupported\n"); #else - CORRADE_COMPARE(out.str(), "TextureTools::DistanceField: output texture format not framebuffer-drawable: GL::Framebuffer::Status::IncompleteAttachment\n"); + CORRADE_COMPARE(out.str(), "TextureTools::DistanceFieldGL: output texture format not framebuffer-drawable: GL::Framebuffer::Status::IncompleteAttachment\n"); #endif } @@ -513,7 +513,7 @@ void DistanceFieldGLTest::sizeRatioNotMultipleOfTwo() { GL::Texture2D outputInvalid; outputInvalid.setStorage(1, GL::textureFormat(PixelFormat::RGBA8Unorm), {23*2, 23*2}); - DistanceField distanceField{4}; + DistanceFieldGL distanceField{4}; /* This should be fine */ distanceField(input, output, {{}, Vector2i{23}} @@ -552,11 +552,11 @@ void DistanceFieldGLTest::sizeRatioNotMultipleOfTwo() { #endif ); CORRADE_COMPARE(out.str(), - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got {322, 322} and {46, 46}\n" - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got {322, 322} and {46, 23}\n" - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got {322, 322} and {23, 46}\n" - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got {322, 322} and {22, 23}\n" - "TextureTools::DistanceField: expected input and output size ratio to be a multiple of 2, got {322, 322} and {23, 22}\n"); + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got {322, 322} and {46, 46}\n" + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got {322, 322} and {46, 23}\n" + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got {322, 322} and {23, 46}\n" + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got {322, 322} and {22, 23}\n" + "TextureTools::DistanceFieldGL: expected input and output size ratio to be a multiple of 2, got {322, 322} and {23, 22}\n"); } #ifndef MAGNUM_TARGET_WEBGL @@ -620,7 +620,7 @@ void DistanceFieldGLTest::benchmark() { MAGNUM_VERIFY_NO_GL_ERROR(); - DistanceField distanceField{32}; + DistanceFieldGL distanceField{32}; CORRADE_BENCHMARK(50) { distanceField(input, framebuffer, {{}, Vector2i{64}} diff --git a/src/Magnum/TextureTools/Test/DistanceFieldTest.cpp b/src/Magnum/TextureTools/Test/DistanceFieldGL_Test.cpp similarity index 79% rename from src/Magnum/TextureTools/Test/DistanceFieldTest.cpp rename to src/Magnum/TextureTools/Test/DistanceFieldGL_Test.cpp index 07707c406..fd7082735 100644 --- a/src/Magnum/TextureTools/Test/DistanceFieldTest.cpp +++ b/src/Magnum/TextureTools/Test/DistanceFieldGL_Test.cpp @@ -25,30 +25,30 @@ #include -#include "Magnum/TextureTools/DistanceField.h" +#include "Magnum/TextureTools/DistanceFieldGL.h" namespace Magnum { namespace TextureTools { namespace Test { namespace { -struct DistanceFieldTest: TestSuite::Tester { - explicit DistanceFieldTest(); +struct DistanceFieldGL_Test: TestSuite::Tester { + explicit DistanceFieldGL_Test(); void constructNoCreate(); }; -DistanceFieldTest::DistanceFieldTest() { - addTests({&DistanceFieldTest::constructNoCreate}); +DistanceFieldGL_Test::DistanceFieldGL_Test() { + addTests({&DistanceFieldGL_Test::constructNoCreate}); } -void DistanceFieldTest::constructNoCreate() { - DistanceField distanceField{NoCreate}; +void DistanceFieldGL_Test::constructNoCreate() { + DistanceFieldGL distanceField{NoCreate}; /* Shouldn't crash or try to acces GL */ CORRADE_VERIFY(true); /* Implicit construction is not allowed */ - CORRADE_VERIFY(!std::is_convertible::value); + CORRADE_VERIFY(!std::is_convertible::value); } }}}} -CORRADE_TEST_MAIN(Magnum::TextureTools::Test::DistanceFieldTest) +CORRADE_TEST_MAIN(Magnum::TextureTools::Test::DistanceFieldGL_Test) diff --git a/src/Magnum/TextureTools/distancefieldconverter.cpp b/src/Magnum/TextureTools/distancefieldconverter.cpp index e8a4beb86..90553ed65 100644 --- a/src/Magnum/TextureTools/distancefieldconverter.cpp +++ b/src/Magnum/TextureTools/distancefieldconverter.cpp @@ -39,7 +39,7 @@ #include "Magnum/GL/Renderer.h" #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" -#include "Magnum/TextureTools/DistanceField.h" +#include "Magnum/TextureTools/DistanceFieldGL.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImageConverter.h" #include "Magnum/Trade/ImageData.h" @@ -239,7 +239,7 @@ int DistanceFieldConverter::exec() { /* Do it */ Debug() << "Converting image of size" << image->size() << "to distance field..."; - TextureTools::DistanceField{args.value("radius")}(input, output, rectangle, image->size()); + TextureTools::DistanceFieldGL{args.value("radius")}(input, output, rectangle, image->size()); /* Save image */ Image2D result{PixelFormat::R8Unorm};