I have a problem when using LWJGL and STB TrueType in IntelliJ.
When I now try to create a bitmap and put everything in Main ('Code 1' below), everything works fine.
As soon as I try to split this up and create an OpenGL context (If I split it up without creating an OpenGL context it works fine as well), the loaded font gets corrupted somehow and either the program crashes with an ACCESS_VIOLATION or runs without generating the bitmap. You can see the broken code as 'Code 2' below.
The wrong behaviour though only occurs when running with the java run arguments, that IntelliJ uses - either via Intellij Run or via Console.
It doesnt occur dont happen when building into a JAR and running this.
The problematic argument is the following. If this one is missing in the console, it runs.
-javaagent:<INTELLIJ_HOME>\lib\idea_rt.jar=52850:<INTELLIJ_HOME>\bin
I have read here that the idea_rt.jar file "is needed to provide graceful shutdown/exit/stacktrace features" and therefore I do not want to disable it in IntelliJ.
NOTE: In the broken code ('Code 2' below) you will notice an 'unnecessary' line
ByteBuffer data2 = loadByteBufferFromResource("/fonts/arial.ttf");
which simulates loading multiple fonts. If I only load one font, everything works fine.
You will also notice the OpenGL context creation in code 2, which also seems to be a cause of the problem (as I described above)
Code 1 (works)
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL;
import org.lwjgl.stb.STBTTFontinfo;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints;
import static org.lwjgl.glfw.GLFW.glfwInit;
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
import static org.lwjgl.stb.STBTruetype.stbtt_GetCodepointBitmap;
import static org.lwjgl.stb.STBTruetype.stbtt_InitFont;
public class STBTTExampleOnlyMain {
private static ByteBuffer loadByteBufferFromResource(String resource) throws IOException {
try(InputStream stream = STBTTExampleOnlyMain .class.getResourceAsStream(resource)) {
byte[] bytes = stream.readAllBytes();
ByteBuffer buffer = BufferUtils.createByteBuffer(bytes.length);
buffer.put(bytes);
buffer.flip();
return buffer;
}
}
public static void main(String[] args) throws IOException {
ByteBuffer data = loadByteBufferFromResource("/fonts/arial.ttf");
ByteBuffer data2 = loadByteBufferFromResource("/fonts/arial.ttf");
STBTTFontinfo font = STBTTFontinfo.create();
stbtt_InitFont(font, data);
IntBuffer bufWidth = BufferUtils.createIntBuffer(1);
IntBuffer bufHeight = BufferUtils.createIntBuffer(1);
ByteBuffer bitmap = stbtt_GetCodepointBitmap(font, 0, 1, 'a', bufWidth, bufHeight, null, null);
System.out.println(bitmap);
}
}
Code 2 (broken)
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL;
import org.lwjgl.stb.STBTTFontinfo;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import static org.lwjgl.glfw.GLFW.glfwCreateWindow;
import static org.lwjgl.glfw.GLFW.glfwDefaultWindowHints;
import static org.lwjgl.glfw.GLFW.glfwInit;
import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent;
import static org.lwjgl.stb.STBTruetype.stbtt_GetCodepointBitmap;
import static org.lwjgl.stb.STBTruetype.stbtt_InitFont;
public class STBTTExample {
private static final Map<Integer, STBTTFontinfo> fontMap = new HashMap<>();
private static ByteBuffer loadByteBufferFromResource(String resource) throws IOException {
try(InputStream stream = STBTTExample.class.getResourceAsStream(resource)) {
byte[] bytes = stream.readAllBytes();
ByteBuffer buffer = BufferUtils.createByteBuffer(bytes.length);
buffer.put(bytes);
buffer.flip();
return buffer;
}
}
private static void initFont() throws IOException {
ByteBuffer data = loadByteBufferFromResource("/fonts/arial.ttf");
ByteBuffer data2 = loadByteBufferFromResource("/fonts/arial.ttf");
STBTTFontinfo font = STBTTFontinfo.create();
stbtt_InitFont(font, data);
fontMap.put(0, font);
}
public static void main(String[] args) throws IOException {
initFont();
glfwInit();
glfwDefaultWindowHints();
long windowHandle = glfwCreateWindow(800, 600, "Test", 0, 0);
glfwMakeContextCurrent(windowHandle);
GL.createCapabilities();
IntBuffer bufWidth = BufferUtils.createIntBuffer(1);
IntBuffer bufHeight = BufferUtils.createIntBuffer(1);
ByteBuffer bitmap = stbtt_GetCodepointBitmap(fontMap.get(0), 0, 1, 'a', bufWidth, bufHeight, null, null);
System.out.println(bitmap);
}
}
How can fix the problem of not being able to run the program out of IntelliJ when working with text rendering?
Am I maybe misunderstanding the STBTT library and actually can't work with fonts this way?
Any help would be appreciated to understand what is wrong and fix this.
I have asked the question in the LWJGL forum and got it solved.
The STBTTFontinfo object contains metadata only. The actual font data is NOT copied from the ByteBuffer you pass to stbtt_InitFont. Only its memory address is copied and the font data is accessed via that pointer when necessary. The segfault you're seeing happens because you don't store a reference to the ByteBuffer anywhere, it is GCed/deallocated by the time you try to use the font. An easy fix would be to change your Font class to also store the ByteBuffer.
You can look at the post here.