Browse Source

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.
pull/650/head
Vladimír Vondruš 2 years ago
parent
commit
2f3d7c0482
  1. 22
      doc/changelog.dox
  2. 2
      src/Magnum/Shaders/DistanceFieldVectorGL.h
  3. 6
      src/Magnum/Text/DistanceFieldGlyphCache.cpp
  4. 6
      src/Magnum/Text/DistanceFieldGlyphCache.h
  5. 2
      src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp
  6. 6
      src/Magnum/TextureTools/CMakeLists.txt
  7. 167
      src/Magnum/TextureTools/DistanceField.h
  8. 24
      src/Magnum/TextureTools/DistanceFieldGL.cpp
  9. 195
      src/Magnum/TextureTools/DistanceFieldGL.h
  10. 2
      src/Magnum/TextureTools/Test/CMakeLists.txt
  11. 42
      src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp
  12. 18
      src/Magnum/TextureTools/Test/DistanceFieldGL_Test.cpp
  13. 4
      src/Magnum/TextureTools/distancefieldconverter.cpp

22
doc/changelog.dox

@ -426,7 +426,7 @@ See also:
- New @ref TextureTools::atlasTextureCoordinateTransformation() helper for - New @ref TextureTools::atlasTextureCoordinateTransformation() helper for
creating an appropriate texture coordinate transformation matrix for creating an appropriate texture coordinate transformation matrix for
textures placed into an atlas 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 @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; easier ability to download the resulting image on OpenGL ES platforms;
the @ref magnum-distancefieldconverter "magnum-distancefieldconverter" the @ref magnum-distancefieldconverter "magnum-distancefieldconverter"
@ -862,7 +862,7 @@ See also:
@subsubsection changelog-latest-changes-texturetools TextureTools library @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 constructor allowing to construct the object without a GL context present
@subsubsection changelog-latest-changes-trade Trade library @subsubsection changelog-latest-changes-trade Trade library
@ -1183,7 +1183,7 @@ See also:
propagate errors when delegating to propagate errors when delegating to
@ref Text::AbstractFontConverter::exportFontToSingleData() / @ref Text::AbstractFontConverter::exportFontToSingleData() /
@ref Text::AbstractFontConverter::exportGlyphCacheToSingleData() @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 slightly shifted, which also makes the output invariant to flips and
transpositions transpositions
- @ref Trade::MeshData::attributeData(UnsignedInt) const was not correctly - @ref Trade::MeshData::attributeData(UnsignedInt) const was not correctly
@ -1423,6 +1423,10 @@ See also:
@ref TextureTools::AtlasLandfill, which has a vastly better packing @ref TextureTools::AtlasLandfill, which has a vastly better packing
efficiency, supports incremental packing and doesn't force the caller to efficiency, supports incremental packing and doesn't force the caller to
use a @ref std::vector 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 - @cpp Trade::AbstractMaterialData @ce as well as its containing header
`Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new `Magnum/Trade/AbstractMaterialData.h` is now a deprecated alias to the new
and more flexible @ref Trade::MaterialData. and more flexible @ref Trade::MaterialData.
@ -1806,8 +1810,8 @@ See also:
translation like in the original case. Additionally, the `*Integer` translation like in the original case. Additionally, the `*Integer`
variants now only round the axis where division by 2 happened for the variants now only round the axis where division by 2 happened for the
middle / center alignment, with the other axis kept unchanged. middle / center alignment, with the other axis kept unchanged.
- @ref TextureTools::DistanceField now asserts that the output texture has a - @ref TextureTools::DistanceFieldGL now asserts that the output texture has
framebuffer-drawable format. Before it only printed an error message and a framebuffer-drawable format. Before it only printed an error message and
silently did nothing, causing a hard-to-track error. silently did nothing, causing a hard-to-track error.
- The @ref Trade::AnimationTrackTarget enum was extended from 8 to 16 bits to - The @ref Trade::AnimationTrackTarget enum was extended from 8 to 16 bits to
provide more room for custom targets, consistently with 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 @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) contexts (which have broken @glsl gl_VertexID @ce)
@subsubsection changelog-2019-10-changes-platform Platform libraries @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 @subsubsection changelog-2019-01-changes-texturetools TextureTools library
- Further performance and output quality improvements for - Further performance and output quality improvements for
@ref TextureTools::DistanceField, making the ES2/WebGL 1 consistent with @cpp TextureTools::DistanceField @ce, making the ES2/WebGL 1 consistent
desktop and speeding up the processing to take only 60% of the time 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 compared to before. It's now also possible to reuse the internal state for
batch processing. batch processing.
@ -3664,7 +3668,7 @@ Released 2019-02-04, tagged as
@ref Corrade/Containers/PointerStl.h header explicitly. @ref Corrade/Containers/PointerStl.h header explicitly.
- @cpp TextureTools::distanceField() @ce is deprecated due to inefficiency of - @cpp TextureTools::distanceField() @ce is deprecated due to inefficiency of
its statelessness when doing batch processing. Use the 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 - @cpp Platform::GlfwApplication::KeyEvent::Key::Smicolon @ce has a typo and
so is deprecated in favor of so is deprecated in favor of
@ref Platform::GlfwApplication::KeyEvent::Key::Semicolon @ref Platform::GlfwApplication::KeyEvent::Key::Semicolon

2
src/Magnum/Shaders/DistanceFieldVectorGL.h

@ -61,7 +61,7 @@ namespace Implementation {
@m_since_latest @m_since_latest
Renders vector graphics in a form of signed distance field. See 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 simpler variant of this shader. Note that the final rendered outlook will
greatly depend on radius of input distance field and value passed to greatly depend on radius of input distance field and value passed to
@ref setSmoothness(). You need to provide @ref Position and @ref setSmoothness(). You need to provide @ref Position and

6
src/Magnum/Text/DistanceFieldGlyphCache.cpp

@ -35,7 +35,7 @@
#endif #endif
#include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/TextureFormat.h"
#include "Magnum/Math/Range.h" #include "Magnum/Math/Range.h"
#include "Magnum/TextureTools/DistanceField.h" #include "Magnum/TextureTools/DistanceFieldGL.h"
namespace Magnum { namespace Text { namespace Magnum { namespace Text {
@ -57,7 +57,7 @@ DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& size, const Vec
processedSize, Vector2i(radius)}, processedSize, Vector2i(radius)},
_distanceField{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 checked during construction already instead of only later during the
setImage() call */ setImage() call */
CORRADE_ASSERT(size % processedSize == Vector2i{0} && 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 paddedMin = image.storage().skip().xy();
const Vector2i paddedMax = image.size() + 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 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 to perform pixel addressing correctly. That might not always be the
case with the rectangle passed to flushImage(), so round the case with the rectangle passed to flushImage(), so round the

6
src/Magnum/Text/DistanceFieldGlyphCache.h

@ -33,7 +33,7 @@
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
#include "Magnum/Text/GlyphCache.h" #include "Magnum/Text/GlyphCache.h"
#include "Magnum/TextureTools/DistanceField.h" #include "Magnum/TextureTools/DistanceFieldGL.h"
namespace Magnum { namespace Text { 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 @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
for more information. for more information.
@see @ref TextureTools::DistanceField @see @ref TextureTools::DistanceFieldGL
*/ */
class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache {
public: public:
@ -167,7 +167,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache {
MAGNUM_TEXT_LOCAL Image3D doProcessedImage() override; MAGNUM_TEXT_LOCAL Image3D doProcessedImage() override;
#endif #endif
TextureTools::DistanceField _distanceField; TextureTools::DistanceFieldGL _distanceField;
}; };
}} }}

2
src/Magnum/Text/Test/DistanceFieldGlyphCacheGLTest.cpp

@ -177,7 +177,7 @@ void DistanceFieldGlyphCacheGLTest::constructSizeRatioNotMultipleOfTwo() {
/* This should be fine */ /* This should be fine */
DistanceFieldGlyphCache{Vector2i{23*14}, Vector2i{23}, 4}; 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; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
DistanceFieldGlyphCache{Vector2i{23*14}, Vector2i{23*2}, 4}; DistanceFieldGlyphCache{Vector2i{23*14}, Vector2i{23*2}, 4};

6
src/Magnum/TextureTools/CMakeLists.txt

@ -60,10 +60,14 @@ if(MAGNUM_TARGET_GL)
endif() endif()
list(APPEND MagnumTextureTools_GracefulAssert_SRCS list(APPEND MagnumTextureTools_GracefulAssert_SRCS
DistanceField.cpp DistanceFieldGL.cpp
${MagnumTextureTools_RESOURCES}) ${MagnumTextureTools_RESOURCES})
list(APPEND MagnumTextureTools_HEADERS DistanceFieldGL.h)
if(MAGNUM_BUILD_DEPRECATED)
list(APPEND MagnumTextureTools_HEADERS DistanceField.h) list(APPEND MagnumTextureTools_HEADERS DistanceField.h)
endif()
endif() endif()
# TextureTools library # TextureTools library

167
src/Magnum/TextureTools/DistanceField.h

@ -25,171 +25,34 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#ifdef MAGNUM_BUILD_DEPRECATED
/** @file /** @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 <Corrade/Containers/Pointer.h>
#include "Magnum/Magnum.h"
#include "Magnum/GL/GL.h"
#ifndef MAGNUM_TARGET_GLES
#include "Magnum/Math/Vector2.h"
#endif #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 #include "Magnum/configure.h"
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 */ #ifdef MAGNUM_BUILD_DEPRECATED
DistanceField& operator=(const DistanceField&) = delete; #include <Corrade/Utility/Macros.h>
/** @brief Move constructor */ #include "Magnum/TextureTools/DistanceFieldGL.h"
DistanceField& operator=(DistanceField&&) noexcept;
/** @brief Max lookup radius */ CORRADE_DEPRECATED_FILE("use Magnum/TextureTools/DistanceFieldGL.h and the DistanceFieldGL class instead")
UnsignedInt radius() const;
/** namespace Magnum { namespace TextureTools {
* @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 @copybrief DistanceFieldGL
* @brief Calculate the distance field to a texture * @m_deprecated_since_latest Use @ref DistanceFieldGL instead.
* @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 typedef CORRADE_DEPRECATED("use DistanceFieldGL instead") DistanceFieldGL DistanceField;
#ifndef MAGNUM_TARGET_GLES
= {}
#endif
);
private:
struct State;
Containers::Pointer<State> _state;
};
}} }}
#else #else
#error this header is available only in the OpenGL build #error use Magnum/TextureTools/DistanceFieldGL.h and the DistanceFieldGL class instead
#endif #endif
#endif #endif

24
src/Magnum/TextureTools/DistanceField.cpp → src/Magnum/TextureTools/DistanceFieldGL.cpp

@ -23,7 +23,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "DistanceField.h" #include "DistanceFieldGL.h"
#include <Corrade/Containers/Iterable.h> #include <Corrade/Containers/Iterable.h>
#include <Corrade/Containers/String.h> #include <Corrade/Containers/String.h>
@ -140,7 +140,7 @@ DistanceFieldShader::DistanceFieldShader(const UnsignedInt radius) {
} }
struct DistanceField::State { struct DistanceFieldGL::State {
explicit State(UnsignedInt radius): shader{radius}, radius{radius} {} explicit State(UnsignedInt radius): shader{radius}, radius{radius} {}
DistanceFieldShader shader; DistanceFieldShader shader;
@ -148,7 +148,7 @@ struct DistanceField::State {
GL::Mesh mesh; 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 #ifndef MAGNUM_TARGET_GLES
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::framebuffer_object); MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::framebuffer_object);
#endif #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 #ifdef MAGNUM_TARGET_GLES
imageSize imageSize
#endif #endif
@ -193,14 +193,14 @@ void DistanceField::operator()(GL::Texture2D& input, GL::Framebuffer& output, co
#endif #endif
CORRADE_ASSERT(output.checkStatus(GL::FramebufferTarget::Draw) == GL::Framebuffer::Status::Complete, 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 /* 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 multiple of 2, causing output pixel *centers* to be aligned with input
pixel *edges* */ pixel *edges* */
CORRADE_ASSERT(imageSize % rectangle.size() == Vector2i{0} && CORRADE_ASSERT(imageSize % rectangle.size() == Vector2i{0} &&
(imageSize/rectangle.size()) % 2 == 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 */ /* Save existing viewport to restore it back after */
const Range2Di previousViewport = output.viewport(); const Range2Di previousViewport = output.viewport();
@ -218,7 +218,7 @@ void DistanceField::operator()(GL::Texture2D& input, GL::Framebuffer& output, co
output.setViewport(previousViewport); 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 #ifdef MAGNUM_TARGET_GLES
imageSize imageSize
#endif #endif

195
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š <mosra@centrum.cz>
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 <Corrade/Containers/Pointer.h>
#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> _state;
};
}}
#else
#error this header is available only in the OpenGL build
#endif
#endif

2
src/Magnum/TextureTools/Test/CMakeLists.txt

@ -88,7 +88,7 @@ else()
endif() endif()
if(MAGNUM_TARGET_GL) 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) if(MAGNUM_BUILD_GL_TESTS)
# Otherwise CMake complains that Corrade::PluginManager is not found, wtf # Otherwise CMake complains that Corrade::PluginManager is not found, wtf

42
src/Magnum/TextureTools/Test/DistanceFieldGLTest.cpp

@ -50,7 +50,7 @@
#include "Magnum/GL/PixelFormat.h" #include "Magnum/GL/PixelFormat.h"
#include "Magnum/GL/Texture.h" #include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/TextureFormat.h"
#include "Magnum/TextureTools/DistanceField.h" #include "Magnum/TextureTools/DistanceFieldGL.h"
#include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/ImageData.h"
@ -135,27 +135,27 @@ DistanceFieldGLTest::DistanceFieldGLTest() {
} }
void DistanceFieldGLTest::construct() { void DistanceFieldGLTest::construct() {
DistanceField distanceField{32}; DistanceFieldGL distanceField{32};
CORRADE_COMPARE(distanceField.radius(), 32); CORRADE_COMPARE(distanceField.radius(), 32);
} }
void DistanceFieldGLTest::constructCopy() { void DistanceFieldGLTest::constructCopy() {
CORRADE_VERIFY(!std::is_copy_constructible<DistanceField>{}); CORRADE_VERIFY(!std::is_copy_constructible<DistanceFieldGL>{});
CORRADE_VERIFY(!std::is_copy_assignable<DistanceField>{}); CORRADE_VERIFY(!std::is_copy_assignable<DistanceFieldGL>{});
} }
void DistanceFieldGLTest::constructMove() { void DistanceFieldGLTest::constructMove() {
DistanceField a{16}; DistanceFieldGL a{16};
DistanceField b = Utility::move(a); DistanceFieldGL b = Utility::move(a);
CORRADE_COMPARE(b.radius(), 16); CORRADE_COMPARE(b.radius(), 16);
DistanceField c{8}; DistanceFieldGL c{8};
c = Utility::move(b); c = Utility::move(b);
CORRADE_COMPARE(c.radius(), 16); CORRADE_COMPARE(c.radius(), 16);
CORRADE_VERIFY(std::is_nothrow_move_constructible<DistanceField>::value); CORRADE_VERIFY(std::is_nothrow_move_constructible<DistanceFieldGL>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<DistanceField>::value); CORRADE_VERIFY(std::is_nothrow_move_assignable<DistanceFieldGL>::value);
} }
void DistanceFieldGLTest::runTexture() { void DistanceFieldGLTest::runTexture() {
@ -236,7 +236,7 @@ void DistanceFieldGLTest::runTexture() {
output.setSubImage(0, {}, ImageView2D{outputPixelFormat, outputPixelType, data.size, output.setSubImage(0, {}, ImageView2D{outputPixelFormat, outputPixelType, data.size,
Containers::Array<char>{DirectInit, std::size_t(data.size.product()*GL::pixelFormatSize(outputPixelFormat, outputPixelType)), '\x66'}}); Containers::Array<char>{DirectInit, std::size_t(data.size.product()*GL::pixelFormatSize(outputPixelFormat, outputPixelType)), '\x66'}});
DistanceField distanceField{32}; DistanceFieldGL distanceField{32};
CORRADE_COMPARE(distanceField.radius(), 32); CORRADE_COMPARE(distanceField.radius(), 32);
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
@ -389,7 +389,7 @@ void DistanceFieldGLTest::runFramebuffer() {
GL::Renderer::setClearColor(0x1f1f1f_rgbf); GL::Renderer::setClearColor(0x1f1f1f_rgbf);
#endif #endif
DistanceField distanceField{32}; DistanceFieldGL distanceField{32};
CORRADE_COMPARE(distanceField.radius(), 32); CORRADE_COMPARE(distanceField.radius(), 32);
MAGNUM_VERIFY_NO_GL_ERROR(); 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}}); output.setImage(0, GL::TextureFormat::RGB9E5, ImageView2D{GL::PixelFormat::RGB, GL::PixelType::UnsignedInt5999Rev, Vector2i{4}});
#endif #endif
DistanceField distanceField{4}; DistanceFieldGL distanceField{4};
std::ostringstream out; std::ostringstream out;
Error redirectError{&out}; Error redirectError{&out};
@ -488,9 +488,9 @@ void DistanceFieldGLTest::formatNotDrawable() {
#endif #endif
); );
#ifndef MAGNUM_TARGET_GLES #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 #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 #endif
} }
@ -513,7 +513,7 @@ void DistanceFieldGLTest::sizeRatioNotMultipleOfTwo() {
GL::Texture2D outputInvalid; GL::Texture2D outputInvalid;
outputInvalid.setStorage(1, GL::textureFormat(PixelFormat::RGBA8Unorm), {23*2, 23*2}); outputInvalid.setStorage(1, GL::textureFormat(PixelFormat::RGBA8Unorm), {23*2, 23*2});
DistanceField distanceField{4}; DistanceFieldGL distanceField{4};
/* This should be fine */ /* This should be fine */
distanceField(input, output, {{}, Vector2i{23}} distanceField(input, output, {{}, Vector2i{23}}
@ -552,11 +552,11 @@ void DistanceFieldGLTest::sizeRatioNotMultipleOfTwo() {
#endif #endif
); );
CORRADE_COMPARE(out.str(), 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::DistanceFieldGL: 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::DistanceFieldGL: 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::DistanceFieldGL: 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::DistanceFieldGL: 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 {23, 22}\n");
} }
#ifndef MAGNUM_TARGET_WEBGL #ifndef MAGNUM_TARGET_WEBGL
@ -620,7 +620,7 @@ void DistanceFieldGLTest::benchmark() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
DistanceField distanceField{32}; DistanceFieldGL distanceField{32};
CORRADE_BENCHMARK(50) { CORRADE_BENCHMARK(50) {
distanceField(input, framebuffer, {{}, Vector2i{64}} distanceField(input, framebuffer, {{}, Vector2i{64}}

18
src/Magnum/TextureTools/Test/DistanceFieldTest.cpp → src/Magnum/TextureTools/Test/DistanceFieldGL_Test.cpp

@ -25,30 +25,30 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/TextureTools/DistanceField.h" #include "Magnum/TextureTools/DistanceFieldGL.h"
namespace Magnum { namespace TextureTools { namespace Test { namespace { namespace Magnum { namespace TextureTools { namespace Test { namespace {
struct DistanceFieldTest: TestSuite::Tester { struct DistanceFieldGL_Test: TestSuite::Tester {
explicit DistanceFieldTest(); explicit DistanceFieldGL_Test();
void constructNoCreate(); void constructNoCreate();
}; };
DistanceFieldTest::DistanceFieldTest() { DistanceFieldGL_Test::DistanceFieldGL_Test() {
addTests({&DistanceFieldTest::constructNoCreate}); addTests({&DistanceFieldGL_Test::constructNoCreate});
} }
void DistanceFieldTest::constructNoCreate() { void DistanceFieldGL_Test::constructNoCreate() {
DistanceField distanceField{NoCreate}; DistanceFieldGL distanceField{NoCreate};
/* Shouldn't crash or try to acces GL */ /* Shouldn't crash or try to acces GL */
CORRADE_VERIFY(true); CORRADE_VERIFY(true);
/* Implicit construction is not allowed */ /* Implicit construction is not allowed */
CORRADE_VERIFY(!std::is_convertible<NoCreateT, DistanceField>::value); CORRADE_VERIFY(!std::is_convertible<NoCreateT, DistanceFieldGL>::value);
} }
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::TextureTools::Test::DistanceFieldTest) CORRADE_TEST_MAIN(Magnum::TextureTools::Test::DistanceFieldGL_Test)

4
src/Magnum/TextureTools/distancefieldconverter.cpp

@ -39,7 +39,7 @@
#include "Magnum/GL/Renderer.h" #include "Magnum/GL/Renderer.h"
#include "Magnum/GL/Texture.h" #include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/TextureFormat.h"
#include "Magnum/TextureTools/DistanceField.h" #include "Magnum/TextureTools/DistanceFieldGL.h"
#include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/AbstractImageConverter.h" #include "Magnum/Trade/AbstractImageConverter.h"
#include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/ImageData.h"
@ -239,7 +239,7 @@ int DistanceFieldConverter::exec() {
/* Do it */ /* Do it */
Debug() << "Converting image of size" << image->size() << "to distance field..."; Debug() << "Converting image of size" << image->size() << "to distance field...";
TextureTools::DistanceField{args.value<UnsignedInt>("radius")}(input, output, rectangle, image->size()); TextureTools::DistanceFieldGL{args.value<UnsignedInt>("radius")}(input, output, rectangle, image->size());
/* Save image */ /* Save image */
Image2D result{PixelFormat::R8Unorm}; Image2D result{PixelFormat::R8Unorm};

Loading…
Cancel
Save