@ -28,6 +28,7 @@
/** @file
* @ brief Class @ ref Magnum : : TextureTools : : DistanceFieldGL
* @ m_since_latest
*/
# include "Magnum/configure.h"
@ -43,51 +44,144 @@ namespace Magnum { namespace TextureTools {
/**
@ brief Create a signed distance field using OpenGL
@ m_since_latest
Converts a high - resolution black and white image ( such as vector artwork or
font glyphs ) to a low - resolution grayscale image with each pixel being a signed
distance to the nearest edge in the original image . Such a distance field image
then occupies much less memory as the spatial resolution is converted to pixel
values amd can be scaled without it being jaggy at small sizes or blurry when
large . It also makes it possible to implement outlining , glow or drop shadow
essentially for free .
@ m_class { m - row }
@ parblock
@ m_div { m - col - m - 6 m - nopadt }
@ image html distancefield - src . png width = 256 px
@ m_enddiv
@ m_div { m - col - m - 6 m - nopadt }
@ image html distancefield - dst . png width = 256 px
@ m_enddiv
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 .
@ endparblock
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 .
You can use the @ ref magnum - distancefieldconverter " magnum-distancefieldconverter "
utility to perform distance field conversion on a command line . Distance field
textures can be rendered with @ ref Shaders : : DistanceFieldVectorGL , this
functionality is also used to implement @ ref Text : : DistanceFieldGlyphCacheGL
for text rendering , which is then exposed in the
@ ref magnum - fontconverter " magnum-fontconverter " utility .
Algorithm 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*
@ note This class is available only if Magnum is compiled with
@ ref MAGNUM_TARGET_GL enabled ( done by default ) . See @ ref building - features
for more information .
@ section TextureTools - DistanceFieldGL - algorithm The algorithm
@ section TextureTools - DistanceFieldGL - usage Example usage
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 following snippet uploads an image to a @ ref GL : : Texture2D , creates a
second smaller @ ref GL : : Texture2D for the output and then performs the distance
field conversion with a radius of @ cpp 12 @ ce pixels and spanning the whole
output image area :
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 .
@ snippet TextureTools - gl . cpp DistanceFieldGL
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*
In the output ( an example of which is shown above on the right , scaled up to
match the original ) , value of @ cpp 1.0 @ ce ( when normalized from the actual
pixel format , so @ cpp 255 @ ce for the @ ref GL : : TextureFormat : : R8 used above )
means that the pixel was originally colored white and nearest black pixel is
further away than the specified radius . Correspondingly , value of @ cpp 0.0 @ ce
means that the pixel was originally black and nearest white pixel is further
away than the radius . Edges are thus at values around @ cpp 0.5 @ ce .
The resulting texture is meant to be used with bilinear filtering , i . e . with
@ ref GL : : SamplerFilter : : Linear . To get the original image back , the GLSL
@ glsl smoothstep ( ) @ ce function can be used as shown in the following snippet ,
with a step around @ cpp 0.5 @ ce and @ cpp smoothness @ ce being a configurable
factor controlling edge smoothness .
@ code { . glsl }
float factor = smoothstep ( 0.5 - smoothness ,
0.5 + smoothness ,
texture ( distanceFieldTexture , coordinates ) . r ) ;
@ endcode
The @ ref Shaders : : DistanceFieldVectorGL implements also outlining , edge dilate ,
erode , and other effects with the distance field input .
@ attention This is a GPU - only implementation , so it expects an active GL
context .
@ section TextureTools - DistanceFieldGL - parameters Parameter tuning
Quality of the generated distance field is affected by two variables - - - the
ratio between input and output size , and the radius . A bigger size ratio will
result in bigger memory savings but at the cost of losing finer detail , so the
choice depends mainly on the content that ' s actually being processed . The image
shown above could get away with being reduced down even eight or sixteen times
without noticeable quality loss , on the other hand vector art consisting of
fine lines or for example CJK glyphs might likely have artifacts already with
the ratio of @ cpp 4 @ ce used above .
The radius should be at least as large as the size ratio in order to contribute
to at least one pixel on every side of an edge in the output , otherwise the
resulting rendering will be extremely blocky . After that , its value is dictated
mainly by the desired use of the output - - - if you need to draw the output with
larger antialiasing smoothness , big outlines or shadows , the radius needs to
get bigger . With the size ratio of @ cpp 4 @ ce and radius of @ cpp 12 @ ce used
above , the output allows for smoothness , outline or other effect ± 3 pixels
around the edge .
Finally , with very large radii you may run into quantization issues with 8 - bit
texture formats , causing again blocky artifacts . A solution is then to use
@ ref GL : : TextureFormat : : R16 instead . Using an even larger format probably won ' t
improve the result any further , and since the distance is normalized to a
@ f $ [ 0 , 1 ] @ f $ range , a floating - point format such as
@ ref GL : : TextureFormat : : R16F would also not , but rather resulting in worse
precision than the 16 - bit normalized integer format .
@ subsection TextureTools - DistanceFieldGL - parameters - rendering Effect of input parameters on final rendered image
In order to ensure consistent look when rendering regardless of the parameters
picked for distance field conversion , the rendering has to take the input size
and radius into account . Assuming @ cpp image . size ( ) @ ce is size of the input
image and ` renderedSize ` is pixel size at which the distance field image is
drawn on the screen , the ` ratio ` calculated below is then distance that
corresponds to one pixel on the screen . Note that the ratio at which the
distance field output is sized down has no effect here , and thus it can be
chosen dynamically to achieve desired quality / memory use tradeoff .
@ snippet TextureTools - gl . cpp DistanceFieldGL - parameters - rendering
For a concrete example , if the input was @ cpp { 256 , 256 } @ ce , it ' s now rendered
at a size of @ cpp { 128 , 128 } @ ce and it was converted with a radius of
@ cpp 12 @ ce , the ` ratio ` will be @ cpp 1.0f / 6 @ ce . I . e . , if you set the shader
` smoothness ` to @ cpp 1.0f / 6 @ ce , the edge smoothness radius will be exactly one
pixel .
@ section TextureTools - DistanceFieldGL - incremental Incremental distance field calculation
Besides converting whole texture at once , it ' s possible to process just a part .
This is mainly useful with use cases like dynamically populated texture
atlases , where it ' d be wasteful to repeatedly process already filled parts .
The * output * area to process is specified with the third argument to
@ ref operator ( ) ( ) ( which was above set to the whole output texture size ) . The
input texture is still taken as a whole , i . e . it ' s assumed that it contains
exactly the data meant to be processed and placed into the output area .
Additionally , to avoid needless OpenGL state changes , it ' s recommended to
supply a @ ref GL : : Framebuffer with the output texture attached so the
implementation doesn ' t need to create a temporary one each time :
@ snippet TextureTools - gl . cpp DistanceFieldGL - incremental
*/
class MAGNUM_TEXTURETOOLS_EXPORT DistanceFieldGL {
public :
/**
* @ brief Constructor
* @ param radius Max lookup radius in the input texture
* @ param radius Distance field calculation radius
*
* Prepares the shader and other internal state for given @ p radius .
*/
@ -129,25 +223,25 @@ class MAGNUM_TEXTURETOOLS_EXPORT DistanceFieldGL {
/** @brief Move constructor */
DistanceFieldGL & operator = ( DistanceFieldGL & & ) noexcept ;
/** @brief Max lookup radius */
/** @brief Distance field calculation radius */
UnsignedInt radius ( ) const ;
/**
* @ brief Calculate the distance field to a framebuffer
* @ brief Calculate distance field to a framebuffer
* @ param input Input texture
* @ param output Output framebuffer
* @ param rectangle Rectangle in output texture where to render
* @ param rectangle Rectangle in the output where to render
* @ param imageSize Input texture size . Needed only for OpenGL ES ,
* on desktop GL the information is gather ed automatically using
* @ ref GL : : Texture2D : : imageSize ( ) .
* on desktop GL the size is queri ed automatically using
* @ ref GL : : Texture2D : : imageSize ( ) and this parameter is ignored .
* @ 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 bu t
* still inefficient supported format is in most cases
* @ gl_extension { EXT , texture_rg } is available ; if not , the smallest ye t
* still quite inefficient supported format is in most cases
* @ ref GL : : TextureFormat : : RGB . The @ ref GL : : TextureFormat : : Luminance
* format usually isn ' t renderable .
*
@ -176,16 +270,17 @@ class MAGNUM_TEXTURETOOLS_EXPORT DistanceFieldGL {
# endif
/**
* @ brief Calculate the distance field to a texture
* @ brief Calculate distance field to a texture
* @ param input Input texture
* @ param output Output texture
* @ param rectangle Rectangle in output texture where to render
* @ param rectangle Rectangle in the output 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 & ) .
* Convenience variant of @ ref operator ( ) ( GL : : Texture2D & , GL : : Framebuffer & , const Range2Di & , const Vector2i & )
* that creates a temporary framebuffer with @ p output attached and
* destroys it again after the operation .
*/
# ifdef DOXYGEN_GENERATING_OUTPUT
void operator ( ) ( GL : : Texture2D & input , GL : : Texture2D & output , const Range2Di & rectangle , const Vector2i & imageSize