Browse Source

TextureTools: ported distanceField() to OpenGL 2.1 and OpenGL ES.

* Older GLSL doesn't have texelFetch() and related things, working
   around it by using classical texture() and normalized floating-point
   coordinates. But that needs to have Texture::imageSize() passed,
   which is not available in OpenGL ES, thus the user must specify it
   explicitly there. On desktop OpenGL that parameter is ignored.
 * Older GLSL doesn't have gl_VertexID, thus vertex buffer must be
   created and vertex data passed expliticly.
 * GLSL ES 2.0 doesn't have one-component texture format and
   TextureFormat::Luminance probably isn't renderable anywhere, thus
   TextureFormat::RGB should be used, although it is inefficient.
 * Checking  for framebuffer completeness, if not complete, nothing is
   done.
 * Re-eabled building of TextureTools library in all ES PKGBUILDs.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
b5743c9447
  1. 1
      PKGBUILD-es2
  2. 1
      PKGBUILD-es2desktop
  3. 1
      PKGBUILD-es3
  4. 2
      src/Text/DistanceFieldGlyphCache.cpp
  5. 128
      src/TextureTools/DistanceField.cpp
  6. 20
      src/TextureTools/DistanceField.h
  7. 86
      src/TextureTools/DistanceFieldShader.frag
  8. 8
      src/TextureTools/DistanceFieldShader.vert

1
PKGBUILD-es2

@ -27,7 +27,6 @@ build() {
-DTARGET_GLES=ON \ -DTARGET_GLES=ON \
-DTARGET_GLES2=ON \ -DTARGET_GLES2=ON \
-DWITH_TEXT=OFF \ -DWITH_TEXT=OFF \
-DWITH_TEXTURETOOLS=OFF \
-DWITH_MAGNUMINFO=OFF \ -DWITH_MAGNUMINFO=OFF \
-DWITH_XEGLAPPLICATION=ON -DWITH_XEGLAPPLICATION=ON
make make

1
PKGBUILD-es2desktop

@ -28,7 +28,6 @@ build() {
-DTARGET_GLES2=ON \ -DTARGET_GLES2=ON \
-DTARGET_DESKTOP_GLES=ON \ -DTARGET_DESKTOP_GLES=ON \
-DWITH_TEXT=OFF \ -DWITH_TEXT=OFF \
-DWITH_TEXTURETOOLS=OFF \
-DWITH_MAGNUMINFO=OFF \ -DWITH_MAGNUMINFO=OFF \
-DWITH_XEGLAPPLICATION=ON -DWITH_XEGLAPPLICATION=ON
make make

1
PKGBUILD-es3

@ -27,7 +27,6 @@ build() {
-DTARGET_GLES=ON \ -DTARGET_GLES=ON \
-DTARGET_GLES2=OFF \ -DTARGET_GLES2=OFF \
-DWITH_TEXT=OFF \ -DWITH_TEXT=OFF \
-DWITH_TEXTURETOOLS=OFF \
-DWITH_MAGNUMINFO=OFF \ -DWITH_MAGNUMINFO=OFF \
-DWITH_XEGLAPPLICATION=ON -DWITH_XEGLAPPLICATION=ON
make make

2
src/Text/DistanceFieldGlyphCache.cpp

@ -57,7 +57,7 @@ void DistanceFieldGlyphCache::setImage(const Vector2i& offset, Image2D* const im
->setImage(0, internalFormat, image); ->setImage(0, internalFormat, image);
/* Create distance field from input texture */ /* Create distance field from input texture */
TextureTools::distanceField(&input, &_texture, Rectanglei::fromSize(offset*scale, image->size()*scale), radius); TextureTools::distanceField(&input, &_texture, Rectanglei::fromSize(offset*scale, image->size()*scale), radius, image->size());
} }
void DistanceFieldGlyphCache::setDistanceFieldImage(const Vector2i& offset, Image2D* const image) { void DistanceFieldGlyphCache::setDistanceFieldImage(const Vector2i& offset, Image2D* const image) {

128
src/TextureTools/DistanceField.cpp

@ -39,6 +39,8 @@ namespace {
class DistanceFieldShader: public AbstractShaderProgram { class DistanceFieldShader: public AbstractShaderProgram {
public: public:
typedef Attribute<0, Vector2> Position;
enum: Int { enum: Int {
TextureLayer = 8 TextureLayer = 8
}; };
@ -50,62 +52,148 @@ class DistanceFieldShader: public AbstractShaderProgram {
return this; return this;
} }
DistanceFieldShader* setScaling(Vector2 scaling) { DistanceFieldShader* setScaling(const Vector2& scaling) {
setUniform(scalingUniform, scaling); setUniform(scalingUniform, scaling);
return this; return this;
} }
DistanceFieldShader* setImageSizeInverted(const Vector2& size) {
setUniform(imageSizeInvertedUniform, size);
return this;
}
private: private:
static const Int radiusUniform = 0, Int radiusUniform,
scalingUniform = 1; scalingUniform,
imageSizeInvertedUniform;
}; };
DistanceFieldShader::DistanceFieldShader() { DistanceFieldShader::DistanceFieldShader(): radiusUniform(0), scalingUniform(1) {
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! */
Utility::Resource rs("MagnumTextureTools"); Utility::Resource rs("MagnumTextureTools");
Shader vert(Version::GL330, Shader::Type::Vertex); #ifndef MAGNUM_TARGET_GLES
vert.addSource(rs.get("DistanceFieldShader.vert")); const Version v = Context::current()->supportedVersion({Version::GL320, Version::GL300, Version::GL210});
#else
const Version v = Context::current()->supportedVersion({Version::GLES300, Version::GLES200});
#endif
Shader vert(v, Shader::Type::Vertex);
vert.addSource(rs.get("compatibility.glsl"))
.addSource(rs.get("DistanceFieldShader.vert"));
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile());
attachShader(vert); attachShader(vert);
Shader frag(Version::GL330, Shader::Type::Fragment); Shader frag(v, Shader::Type::Fragment);
frag.addSource(rs.get("compatibility.glsl")) frag.addSource(rs.get("compatibility.glsl"))
.addSource(rs.get("DistanceFieldShader.frag")); .addSource(rs.get("DistanceFieldShader.frag"));
CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile());
attachShader(frag); attachShader(frag);
/* Older GLSL doesn't have gl_VertexID, vertices must be supplied explicitly */
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isVersionSupported(Version::GL300))
#else
if(!Context::current()->isVersionSupported(Version::GLES300))
#endif
{
bindAttributeLocation(Position::Location, "position");
}
CORRADE_INTERNAL_ASSERT_OUTPUT(link()); CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::explicit_uniform_location>())
#endif
{
radiusUniform = uniformLocation("radius");
scalingUniform = uniformLocation("scaling");
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isVersionSupported(Version::GL300))
#else
if(!Context::current()->isVersionSupported(Version::GLES300))
#endif
{
imageSizeInvertedUniform = uniformLocation("imageSizeInverted");
}
}
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::shading_language_420pack>())
#endif
{
setUniform(uniformLocation("textureData"), TextureLayer);
}
} }
void distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius) { }
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::EXT::framebuffer_object); #ifndef MAGNUM_TARGET_GLES
void distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius, const Vector2i&)
#else
void distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, const Int radius, const Vector2i& imageSize)
#endif
{
#ifndef MAGNUM_TARGET_GLES
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::framebuffer_object);
#endif
/** @todo Disable depth test, blending and then enable it back (if was previously) */ /** @todo Disable depth test, blending and then enable it back (if was previously) */
#ifndef MAGNUM_TARGET_GLES
Vector2i imageSize = input->imageSize(0);
#endif
Framebuffer framebuffer(rectangle); Framebuffer framebuffer(rectangle);
framebuffer.attachTexture2D(Framebuffer::ColorAttachment(0), output, 0); framebuffer.attachTexture2D(Framebuffer::ColorAttachment(0), output, 0);
framebuffer.bind(FramebufferTarget::Draw); framebuffer.bind(FramebufferTarget::Draw);
framebuffer.clear(FramebufferClear::Color);
const Framebuffer::Status status = framebuffer.checkStatus(FramebufferTarget::Draw);
if(status != Framebuffer::Status::Complete) {
Error() << "TextureTools::distanceField(): cannot render to given output texture, unexpected framebuffer status"
<< status;
return;
}
DistanceFieldShader shader; DistanceFieldShader shader;
shader.setRadius(radius) shader.setRadius(radius)
->setScaling(Vector2(input->imageSize(0))/rectangle.size()) ->setScaling(Vector2(imageSize)/rectangle.size())
->use(); ->use();
input->bind(DistanceFieldShader::TextureLayer); input->bind(DistanceFieldShader::TextureLayer);
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isVersionSupported(Version::GL300))
#else
if(!Context::current()->isVersionSupported(Version::GLES300))
#endif
{
shader.setImageSizeInverted(Vector2(1)/imageSize);
}
Mesh mesh; Mesh mesh;
mesh.setPrimitive(Mesh::Primitive::Triangles) mesh.setPrimitive(Mesh::Primitive::Triangles)
->setVertexCount(3) ->setVertexCount(3);
->draw();
/* Older GLSL doesn't have gl_VertexID, vertices must be supplied explicitly */
Buffer buffer;
#ifndef MAGNUM_TARGET_GLES
if(!Context::current()->isVersionSupported(Version::GL300))
#else
if(!Context::current()->isVersionSupported(Version::GLES300))
#endif
{
constexpr Vector2 triangle[] = {
Vector2(-1.0, 1.0),
Vector2(-1.0, -3.0),
Vector2( 3.0, 1.0)
};
buffer.setData(triangle, Buffer::Usage::StaticDraw);
mesh.addVertexBuffer(&buffer, 0, DistanceFieldShader::Position());
}
/* Draw the mesh */
mesh.draw();
} }
}} }}

20
src/TextureTools/DistanceField.h

@ -28,6 +28,9 @@
* @brief Function Magnum::TextureTools::distanceField() * @brief Function Magnum::TextureTools::distanceField()
*/ */
#ifndef MAGNUM_TARGET_GLES
#include "Math/Vector2.h"
#endif
#include "Magnum.h" #include "Magnum.h"
#include "TextureTools/magnumTextureToolsVisibility.h" #include "TextureTools/magnumTextureToolsVisibility.h"
@ -40,6 +43,8 @@ namespace Magnum { namespace TextureTools {
@param output Output texture @param output Output texture
@param rectangle Rectangle in output texture where to render @param rectangle Rectangle in output texture where to render
@param radius Max lookup radius in input texture @param radius Max lookup radius in input texture
@param imageSize Input texture size. Needed only in OpenGL ES, in desktop
OpenGL the information is gathered automatically using Texture::imageSize().
Converts binary image (stored in red channel of @p input) to signed distance 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 field (stored in red channel in @p rectangle of @p output). The purpose of this
@ -66,8 +71,21 @@ and Special Effects, SIGGRAPH 2007,
http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf* http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf*
@attention This is GPU-only implementation, so it expects active context. @attention This is GPU-only implementation, so it expects active context.
@note If internal format of @p output texture is not renderable, this function
prints message to error output and does nothing. In desktop OpenGL and
OpenGL ES 3.0 it's common to render to @ref TextureFormat "TextureFormat::R8".
In OpenGL ES 2.0 you can use @ref TextureFormat "TextureFormat::Red" if
@es_extension{EXT,texture_rg} is available, if not, the smallest but still
inefficient supported format is in most cases @ref TextureFormat "TextureFormat::RGB",
rendering to @ref TextureFormat "TextureFormat::Luminance" is not supported
in most cases.
*/ */
void MAGNUM_TEXTURETOOLS_EXPORT distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, Int radius); #ifndef MAGNUM_TARGET_GLES
void MAGNUM_TEXTURETOOLS_EXPORT distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, Int radius, const Vector2i& imageSize = Vector2i());
#else
void MAGNUM_TEXTURETOOLS_EXPORT distanceField(Texture2D* input, Texture2D* output, const Rectanglei& rectangle, Int radius, const Vector2i& imageSize);
#endif
}} }}

86
src/TextureTools/DistanceFieldShader.frag

@ -22,40 +22,90 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#ifndef NEW_GLSL
#define in varying
#define value gl_FragColor.x
#define const
#define texture texture2D
#endif
#if (defined(GL_ES) && __VERSION__ >= 300) || (!defined(GL_ES) && __VERSION__ >= 150)
#define TEXELFETCH_USABLE
#endif
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0) uniform int radius; layout(location = 0) uniform int radius;
layout(location = 1) uniform vec2 scaling; layout(location = 1) uniform vec2 scaling;
layout(binding = 8) uniform sampler2D texture; layout(binding = 8) uniform sampler2D textureData;
#else
layout(pixel_center_integer) in vec4 gl_FragCoord; uniform lowp int radius;
uniform mediump vec2 scaling;
out float value; uniform lowp sampler2D textureData;
#endif
#ifdef TEXELFETCH_USABLE
layout(pixel_center_integer) in mediump vec4 gl_FragCoord;
#else
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2) uniform vec2 imageSizeInverted;
#else
uniform mediump vec2 imageSizeInverted;
#endif
#endif
#ifdef NEW_GLSL
out lowp float value;
#endif
#ifdef TEXELFETCH_USABLE
mediump ivec2 rotate(const mediump ivec2 vec) {
return mediump ivec2(-vec.y, vec.x);
}
ivec2 rotate(const ivec2 vec) { bool hasValue(const mediump ivec2 position, const mediump ivec2 offset) {
return ivec2(-vec.y, vec.x); return texelFetch(textureData, position+offset, 0).r > 0.5;
}
#else
mediump vec2 rotate(const mediump vec2 vec) {
return mediump vec2(-vec.y, vec.x);
} }
bool hasValue(const ivec2 position, const ivec2 offset) { bool hasValue(const mediump vec2 position, const mediump vec2 offset) {
return texelFetch(texture, position+offset, 0).r > 0.5; return texture(textureData, position+offset).r > 0.5;
} }
#endif
void main() { void main() {
const ivec2 position = ivec2(gl_FragCoord.xy*scaling); #ifdef TEXELFETCH_USABLE
const mediump ivec2 position = ivec2(gl_FragCoord.xy*scaling);
#else
const mediump vec2 position = gl_FragCoord.xy*scaling*imageSizeInverted;
#endif
/* If pixel at the position is inside (1), we are looking for nearest pixel /* 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 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 are looking for nearest pixel inside and the value will be negative
(< 0.5). */ (< 0.5). */
#ifdef TEXELFETCH_USABLE
const bool isInside = hasValue(position, ivec2(0, 0)); const bool isInside = hasValue(position, ivec2(0, 0));
const float sign = isInside ? 1.0 : -1.0; #else
const bool isInside = hasValue(position, vec2(0.0, 0.0));
#endif
const highp float sign = isInside ? 1.0 : -1.0;
/* Minimal found distance is just out of the radius (i.e. infinity) */ /* Minimal found distance is just out of the radius (i.e. infinity) */
float minDistanceSquared = float((radius+1)*(radius+1)); highp float minDistanceSquared = float((radius+1)*(radius+1));
/* Go in circles around the point and find nearest value */ /* Go in circles around the point and find nearest value */
int radiusLimit = radius; int radiusLimit = radius;
for(int i = 1; i <= radiusLimit; ++i) { for(int i = 1; i <= radiusLimit; ++i) {
for(int j = 0, jmax = i*2; j != jmax; ++j) { for(int j = 0, jmax = i*2; j != jmax; ++j) {
const ivec2 offset = {-i+j, i}; #ifdef TEXELFETCH_USABLE
const lowp ivec2 offset = ivec2(-i+j, i);
#else
const lowp vec2 pixelOffset = vec2(float(-i+j), float(i));
const lowp vec2 offset = pixelOffset*imageSizeInverted;
#endif
/* If any of the four values is opposite of what is on the pixel, /* If any of the four values is opposite of what is on the pixel,
we found nearest value */ we found nearest value */
@ -63,7 +113,11 @@ void main() {
hasValue(position, rotate(offset)) == !isInside || hasValue(position, rotate(offset)) == !isInside ||
hasValue(position, rotate(rotate(offset))) == !isInside || hasValue(position, rotate(rotate(offset))) == !isInside ||
hasValue(position, rotate(rotate(rotate(offset)))) == !isInside) { hasValue(position, rotate(rotate(rotate(offset)))) == !isInside) {
const float distanceSquared = dot(vec2(offset), vec2(offset)); #ifdef TEXELFETCH_USABLE
const mediump float distanceSquared = dot(vec2(offset), vec2(offset));
#else
const mediump float distanceSquared = dot(pixelOffset, pixelOffset);
#endif
/* Set smaller distance, if found, or continue with lookup for /* Set smaller distance, if found, or continue with lookup for
smaller */ smaller */
@ -73,7 +127,11 @@ void main() {
/* Set radius limit to max radius which can contain smaller /* Set radius limit to max radius which can contain smaller
value, e.g. for distance 3.5 we can find smaller value even value, e.g. for distance 3.5 we can find smaller value even
in radius 3 */ in radius 3 */
#ifdef NEW_GLSL
radiusLimit = min(radius, int(floor(length(vec2(offset))))); radiusLimit = min(radius, int(floor(length(vec2(offset)))));
#else
radiusLimit = int(min(float(radius), floor(length(vec2(offset)))));
#endif
} }
} }
} }

8
src/TextureTools/DistanceFieldShader.vert

@ -22,7 +22,15 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#ifndef NEW_GLSL
attribute lowp vec4 position;
#endif
void main() { void main() {
#ifdef NEW_GLSL
gl_Position = vec4((gl_VertexID == 2) ? 3.0 : -1.0, gl_Position = vec4((gl_VertexID == 2) ? 3.0 : -1.0,
(gl_VertexID == 1) ? -3.0 : 1.0, 0.0, 1.0); (gl_VertexID == 1) ? -3.0 : 1.0, 0.0, 1.0);
#else
gl_Position = position;
#endif
} }

Loading…
Cancel
Save