Hello Guest

stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character

  • 5 Replies
  • 5546 Views
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':



Hopefully this code sample isn't too bad:

Font.java
Code: [Select]
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
Code: [Select]
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();
}
}
« Last Edit: November 27, 2019, 18:59:04 by WeBeJammin »

*

Offline overlisted

  • *
  • 15
  • #MakeMinecraftOpenSource
Re: stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character
« Reply #1 on: November 27, 2019, 19:06:15 »
Doesn't it repeat because of GL_REPEAT?

Re: stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character
« Reply #2 on: November 27, 2019, 19:08:03 »
I was thinking the same thing, but using GL_CLAMP_TO_BORDER produced the same result.

*

Offline overlisted

  • *
  • 15
  • #MakeMinecraftOpenSource
Re: stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character
« Reply #3 on: November 27, 2019, 21:59:35 »
I tried to use your class (don't care about font texture on a block)...


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
Code: [Select]
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
Code: [Select]
  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);
  }
« Last Edit: November 27, 2019, 22:05:14 by True_han »

Re: stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character
« Reply #4 on: November 27, 2019, 22:32:30 »
Thanks for looking into it a bit. Weird that the texture is so noisy...

*

Offline overlisted

  • *
  • 15
  • #MakeMinecraftOpenSource
Re: stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character
« Reply #5 on: November 28, 2019, 16:57:38 »
Hmm... The official stb_truetype.h says that the texture format should be GL_ALPHA8



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
Code: [Select]
  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



To change the character color you can use a fragment shader.