Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TextureTools: initial rude implementation of distance field computation.
Not yet ported anywhere else than GL >= 3.3 with some GL 4.3 features. Still, this closes #3.
- Loading branch information
Showing
5 changed files
with
234 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> | ||
This file is part of Magnum. | ||
Magnum is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Lesser General Public License version 3 | ||
only, as published by the Free Software Foundation. | ||
Magnum is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Lesser General Public License version 3 for more details. | ||
*/ | ||
|
||
#include "TextureTools/DistanceField.h" | ||
|
||
#include <Utility/Resource.h> | ||
#include "Math/Geometry/Rectangle.h" | ||
#include "AbstractShaderProgram.h" | ||
#include "Extensions.h" | ||
#include "Framebuffer.h" | ||
#include "Mesh.h" | ||
#include "Shader.h" | ||
#include "Texture.h" | ||
|
||
namespace Magnum { namespace TextureTools { | ||
|
||
namespace { | ||
|
||
class DistanceFieldShader: public AbstractShaderProgram { | ||
public: | ||
enum: Int { | ||
TextureLayer = 8 | ||
}; | ||
|
||
explicit DistanceFieldShader(); | ||
|
||
inline DistanceFieldShader* setRadius(Int radius) { | ||
setUniform(radiusUniform, radius); | ||
return this; | ||
} | ||
|
||
inline DistanceFieldShader* setScaling(Vector2 scaling) { | ||
setUniform(scalingUniform, scaling); | ||
return this; | ||
} | ||
|
||
private: | ||
static const Int radiusUniform = 0, | ||
scalingUniform = 1; | ||
}; | ||
|
||
DistanceFieldShader::DistanceFieldShader() { | ||
MAGNUM_ASSERT_VERSION_SUPPORTED(Version::GL330); | ||
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::explicit_attrib_location); | ||
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::explicit_uniform_location); | ||
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::shading_language_420pack); | ||
|
||
/** @todo compatibility! */ | ||
|
||
Corrade::Utility::Resource rs("MagnumTextureTools"); | ||
attachShader(Shader::fromData(Version::GL330, Shader::Type::Vertex, rs.get("DistanceFieldShader.vert"))); | ||
|
||
Shader fragmentShader(Version::GL330, Shader::Type::Fragment); | ||
fragmentShader.addSource(rs.get("compatibility.glsl")); | ||
fragmentShader.addSource(rs.get("DistanceFieldShader.frag")); | ||
attachShader(fragmentShader); | ||
|
||
link(); | ||
} | ||
|
||
} | ||
|
||
void distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius) { | ||
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::framebuffer_object); | ||
|
||
/** @todo Disable depth test and then enable it back (if was previously) */ | ||
|
||
Framebuffer framebuffer(rectangle); | ||
framebuffer.attachTexture2D(Framebuffer::ColorAttachment(0), output, 0); | ||
framebuffer.bind(Framebuffer::Target::Draw); | ||
|
||
DistanceFieldShader shader; | ||
shader.setRadius(radius) | ||
->setScaling(Vector2(input->imageSize(0))/rectangle.size()) | ||
->use(); | ||
|
||
input->bind(DistanceFieldShader::TextureLayer); | ||
|
||
Mesh mesh; | ||
mesh.setPrimitive(Mesh::Primitive::Triangles) | ||
->setVertexCount(3) | ||
->draw(); | ||
} | ||
|
||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
#ifndef Magnum_TextureTools_DistanceField_h | ||
#define Magnum_TextureTools_DistanceField_h | ||
/* | ||
Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz> | ||
This file is part of Magnum. | ||
Magnum is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Lesser General Public License version 3 | ||
only, as published by the Free Software Foundation. | ||
Magnum is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Lesser General Public License version 3 for more details. | ||
*/ | ||
|
||
/** @file | ||
* @brief Function Magnum::TextureTools::distanceField() | ||
*/ | ||
|
||
#include "Magnum.h" | ||
|
||
#include "TextureTools/magnumTextureToolsVisibility.h" | ||
|
||
namespace Magnum { namespace TextureTools { | ||
|
||
/** | ||
@brief Create signed distance field | ||
@param input Input texture | ||
@param output Output texture | ||
@param rectangle Rectangle in output texture where to render | ||
@param radius Max lookup radius in input texture | ||
Converts binary image (stored in red channel of @p input) to signed distance | ||
field (stored in red channel in @p rectangle of @p output). The purpose of this | ||
function is to convert high-resolution binary image (such as vector artwork or | ||
font glyphs) to 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 essentialy for free. | ||
For each pixel inside @p rectangle the algorithm looks at corresponding pixel in | ||
@p input and tries to find nearest pixel of opposite color in area given by | ||
@p radius. Signed distance between the points is then saved as value of given | ||
pixel in @p output. Value of `0` means that the pixel was originally colored | ||
white and nearest black pixel is farther than @p radius, value of `1` 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 `smoothstep()` function with step | ||
around `0.5` to create antialiased edges. Or you can exploit the distance field | ||
features to create many other effects. | ||
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 GPU-only implementation, so it expects active context. | ||
*/ | ||
void MAGNUM_TEXTURETOOLS_EXPORT distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius); | ||
|
||
}} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#line 2 | ||
layout(location = 0) uniform int radius; | ||
layout(location = 1) uniform vec2 scaling; | ||
layout(binding = 8) uniform sampler2D texture; | ||
|
||
layout(pixel_center_integer) in vec4 gl_FragCoord; | ||
|
||
out float value; | ||
|
||
ivec2 rotate(const ivec2 vec) { | ||
return ivec2(-vec.y, vec.x); | ||
} | ||
|
||
bool hasValue(const ivec2 position, const ivec2 offset) { | ||
return texelFetch(texture, position+offset, 0).r > 0.5; | ||
} | ||
|
||
void main() { | ||
const ivec2 position = ivec2(gl_FragCoord.xy*scaling); | ||
|
||
/* If pixel at the position is inside (1), we are looking for nearest pixel | ||
outside and the value will be positive (> 0.5). If it is outside (0), we | ||
are looking for nearest pixel inside and the value will be negative | ||
(< 0.5). */ | ||
const bool isInside = hasValue(position, ivec2(0, 0)); | ||
const float sign = isInside ? 1.0 : -1.0; | ||
|
||
/* Minimal found distance is just out of the radius (i.e. infinity) */ | ||
float minDistanceSquared = float((radius+1)*(radius+1)); | ||
|
||
/* Go in circles around the point and find nearest value */ | ||
int radiusLimit = radius; | ||
for(int i = 1; i <= radiusLimit; ++i) { | ||
for(int j = 0, jmax = i*2; j != jmax; ++j) { | ||
const ivec2 offset = {-i+j, i}; | ||
|
||
/* If any of the four values is opposite of what is on the pixel, | ||
we found nearest value */ | ||
if(hasValue(position, offset) == !isInside || | ||
hasValue(position, rotate(offset)) == !isInside || | ||
hasValue(position, rotate(rotate(offset))) == !isInside || | ||
hasValue(position, rotate(rotate(rotate(offset)))) == !isInside) { | ||
const float distanceSquared = dot(vec2(offset), vec2(offset)); | ||
|
||
/* Set smaller distance, if found, or continue with lookup for | ||
smaller */ | ||
if(minDistanceSquared < distanceSquared) continue; | ||
else minDistanceSquared = distanceSquared; | ||
|
||
/* Set radius limit to max radius which can contain smaller | ||
value, e.g. for distance 3.5 we can find smaller value even | ||
in radius 3 */ | ||
radiusLimit = min(radius, int(floor(length(vec2(offset))))); | ||
} | ||
} | ||
} | ||
|
||
/* Final signed distance, normalized from [-radius-1, radius+1] to [0, 1] */ | ||
value = sign*sqrt(minDistanceSquared)/float(radius*2+2)+0.5; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
void main() { | ||
gl_Position = vec4((gl_VertexID == 2) ? 3.0 : -1.0, | ||
(gl_VertexID == 1) ? -3.0 : 1.0, 0.0, 1.0); | ||
} |