/*
* Copyright (c) 2015, Florian Falkner
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Matthias Mann nor
* the names of its contributors may be used to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.badlogic.gdx.graphics.g2d;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Array;
/** Renders bitmap fonts using distance field textures, see the <a
* href="https://github.com/libgdx/libgdx/wiki/Distance-field-fonts">Distance Field Fonts wiki article</a> for usage. Initialize
* the SpriteBatch with the {@link #createDistanceFieldShader()} shader.
* <p>
* Attention: The batch is flushed before and after each string is rendered.
* @author Florian Falkner */
public class DistanceFieldFont extends BitmapFont {
private float distanceFieldSmoothing;
public DistanceFieldFont (BitmapFontData data, Array<TextureRegion> pageRegions, boolean integer) {
super(data, pageRegions, integer);
}
public DistanceFieldFont (BitmapFontData data, TextureRegion region, boolean integer) {
super(data, region, integer);
}
public DistanceFieldFont (FileHandle fontFile, boolean flip) {
super(fontFile, flip);
}
public DistanceFieldFont (FileHandle fontFile, FileHandle imageFile, boolean flip, boolean integer) {
super(fontFile, imageFile, flip, integer);
}
public DistanceFieldFont (FileHandle fontFile, FileHandle imageFile, boolean flip) {
super(fontFile, imageFile, flip);
}
public DistanceFieldFont (FileHandle fontFile, TextureRegion region, boolean flip) {
super(fontFile, region, flip);
}
public DistanceFieldFont (FileHandle fontFile, TextureRegion region) {
super(fontFile, region);
}
public DistanceFieldFont (FileHandle fontFile) {
super(fontFile);
}
protected void load (BitmapFontData data) {
super.load(data);
// Distance field font rendering requires font texture to be filtered linear.
final Array<TextureRegion> regions = getRegions();
for (TextureRegion region : regions)
region.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
}
@Override
public BitmapFontCache newFontCache () {
return new DistanceFieldFontCache(this, integer);
}
/** @return The distance field smoothing factor for this font. */
public float getDistanceFieldSmoothing () {
return distanceFieldSmoothing;
}
/** @param distanceFieldSmoothing Set the distance field smoothing factor for this font. SpriteBatch needs to have this shader
* set for rendering distance field fonts. */
public void setDistanceFieldSmoothing (float distanceFieldSmoothing) {
this.distanceFieldSmoothing = distanceFieldSmoothing;
}
/** Returns a new instance of the distance field shader, see https://github.com/libgdx/libgdx/wiki/Distance-field-fonts if the
* u_smoothing uniform > 0.0. Otherwise the same code as the default SpriteBatch shader is used. */
static public ShaderProgram createDistanceFieldShader () {
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main() {\n" //
+ " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ " v_color.a = v_color.a * (255.0/254.0);\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ " precision mediump float;\n" //
+ " precision mediump int;\n" //
+ "#endif\n" //
+ "\n" //
+ "uniform sampler2D u_texture;\n" //
+ "uniform float u_smoothing;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main() {\n" //
+ " if (u_smoothing > 0.0) {\n" //
+ " float smoothing = 0.25 / u_smoothing;\n" //
+ " float distance = texture2D(u_texture, v_texCoords).a;\n" //
+ " float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);\n" //
+ " gl_FragColor = vec4(v_color.rgb, alpha * v_color.a);\n" //
+ " } else {\n" //
+ " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n" //
+ " }\n" //
+ "}\n";
ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if (shader.isCompiled() == false)
throw new IllegalArgumentException("Error compiling distance field shader: " + shader.getLog());
return shader;
}
/** Provides a font cache that uses distance field shader for rendering fonts. Attention: breaks batching because uniform is
* needed for smoothing factor, so a flush is performed before and after every font rendering.
* @author Florian Falkner */
static private class DistanceFieldFontCache extends BitmapFontCache {
public DistanceFieldFontCache (DistanceFieldFont font) {
super(font, font.usesIntegerPositions());
}
public DistanceFieldFontCache (DistanceFieldFont font, boolean integer) {
super(font, integer);
}
private float getSmoothingFactor () {
final DistanceFieldFont font = (DistanceFieldFont)super.getFont();
return font.getDistanceFieldSmoothing() * font.getScaleX();
}
private void setSmoothingUniform (Batch spriteBatch, float smoothing) {
spriteBatch.flush();
spriteBatch.getShader().setUniformf("u_smoothing", smoothing);
}
@Override
public void draw (Batch spriteBatch) {
setSmoothingUniform(spriteBatch, getSmoothingFactor());
super.draw(spriteBatch);
setSmoothingUniform(spriteBatch, 0);
}
@Override
public void draw (Batch spriteBatch, int start, int end) {
setSmoothingUniform(spriteBatch, getSmoothingFactor());
super.draw(spriteBatch, start, end);
setSmoothingUniform(spriteBatch, 0);
}
}
}