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};