stbtt_GetCodepointBitmap generating a 4x4 bitmap of the character

Started by WeBeJammin, November 27, 2019, 18:56:56

Previous topic - Next topic

WeBeJammin

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
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();
	}
}

overlisted


WeBeJammin

I was thinking the same thing, but using GL_CLAMP_TO_BORDER produced the same result.

overlisted

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
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);
  }

WeBeJammin

Thanks for looking into it a bit. Weird that the texture is so noisy...

overlisted

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
  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.