I was hoping somebody could give me a clue what I am doing wrong when creating character bitmaps from the STBTruetype library. The generated bitmap seems to be a bitmap representing the character requested, however it is in a 4x4 grid with a slight offset in the negative x asix for each row. I thought I would get back a bitmap representing a single instance of the character. Here is the output for the character 'A':
(https://imgur.com/ivygLBw.png)
Hopefully this code sample isn't too bad:
Font.java
public class Font {
private STBTTFontinfo info;
private float pixelScale;
public Font(final String ttfPath) throws IOException {
final ByteBuffer ttfBuffer = ResourceLoader.toByteBuffer(ttfPath);
info = STBTTFontinfo.create();
stbtt_InitFont(info, ttfBuffer, 0);
pixelScale = stbtt_ScaleForPixelHeight(info, 512);
}
public Character getChar(final char c) {
final int[] w = new int[]{0}, h = new int[]{0}, xoff = new int[]{0}, yoff = new int[]{0};
final ByteBuffer bitmapData = stbtt_GetCodepointBitmap(info, 0, pixelScale, c, w, h, xoff, yoff);
return new Character(bitmapData, w[0], h[0], xoff[0], yoff[0]); // dumb pojo to hold bitmap and dimensions
}
}
FontText.java
public class FontTest {
private Window window = RunnerUtils.setupWindow();
private Font font;
public static void main(String[] args) throws Exception {
new FontTest().run();
}
private void run() throws Exception {
OpenGlState.init(window);
font = new Font("font/roboto/Roboto-Black.ttf");
Character aChar = font.getChar('A');
int textureId = glGenTextures();
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, aChar.getWidth(), aChar.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, aChar.getBitmapData());
OpenglUtils.saveTextureToJpg(textureId, new File("out.png")); // Saves to file, but rendering the bitmap to a quad is the same image
window.destroy();
}
}
Doesn't it repeat because of GL_REPEAT?
I was thinking the same thing, but using GL_CLAMP_TO_BORDER produced the same result.
I tried to use your class (don't care about font texture on a block)...
(https://sun9-66.userapi.com/c854016/v854016200/18bc22/7KFpFglOsKQ.jpg)
It gets more and more noisy every time I run the game. Once it breaks and OpenGL can't process a texture. :o
My Font.java
public class Font {
public STBTTFontinfo info;
public float fontSize = 512;
public Font(String resource) {
this.info = STBTTFontinfo.create();
stbtt_InitFont(info, Utils.loadResource(resource), 0);
}
public Texture getTextureFor(char character) {
try(MemoryStack stack = MemoryStack.stackPush()) {
final IntBuffer width = stack.mallocInt(1);
final IntBuffer height = stack.mallocInt(1);
final ByteBuffer bitmap = stbtt_GetCodepointBitmap(
this.info,
0,
stbtt_ScaleForPixelHeight(this.info, this.fontSize),
character,
width,
height,
null,
null
);
System.out.println(
"char: " + width.get(0) + " " + height.get(0) + " " + character + " " + stbtt_ScaleForPixelHeight(this.info, this.fontSize)
);
final Texture texture = new Texture(
bitmap,
width.get(0),
height.get(0)
);
stbtt_FreeBitmap(bitmap);
return texture;
} catch(Throwable e) {
e.printStackTrace();
return null;
}
}
}
My texture constructor
public Texture(ByteBuffer bitmap, int width, int height) {
this.width = width;
this.height = height;
this.id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, this.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
glGenerateMipmap(GL_TEXTURE_2D);
}
Thanks for looking into it a bit. Weird that the texture is so noisy...
Hmm... The official stb_truetype.h says that the texture format should be GL_ALPHA8
(https://sun9-61.userapi.com/c854016/v854016843/184328/KlxHXF10H64.jpg)
Yaaay! I could make this work! I think we were seeing 4 * 4 letters because of RGBA8-ALPHA8 conflict. The noise also has gone.
Texture constructor
public Texture(ByteBuffer bitmap, int width, int height) {
this.width = width;
this.height = height;
this.id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, this.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// uncomment code below to make gl not antialias minecraft-like fonts
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 8 bpp = 1 byte per pixel
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap);
glGenerateMipmap(GL_TEXTURE_2D);
}
Font class hasn't been changed so you can use it from my previous comment.
Result
(https://sun9-36.userapi.com/c854016/v854016843/1843aa/r7vjPFjR1m8.jpg)
To change the character color you can use a fragment shader.