stb_truetype 计算字符串的维度

stb_truetype calculating the dimensions of a string

我正在使用 stb_truetype 在 OpenGL 上下文中呈现 TrueType 字体。

有没有什么简单的方法可以在渲染之前预先确定字体中字符串的高度和宽度?

我能够通过 LWJGL 3.1.1 绑定到 stb 得到一个大概的答案。

首先,在开始加载字体时调用 STBTruetype.stbtt_BakeFontBitmap(ByteBuffer data, float pixel_height, ByteBuffer pixels, int pw, int ph, int first_char, Buffer chardata) 后,保持 chardata 缓冲区,应该是 STBTTBakedChar.Buffer,同时保持 [=13] =] 这是一个 int.

接下来,如果您要获取 'M' 字符的宽度,例如,调用 chardata.get('M' - first_char).x1() - chardata.get('M' - first_char).x0(),结果应该是字符的宽度。您可以对字符串的每个字符执行此操作,并将所有值加起来作为字符串的总宽度。

我注意到获取 space 字符的宽度得到的结果为 0,因此这应该仅用作近似值。就个人而言,我将 'M' 字符的宽度乘以字符串的总长度以获得字符串宽度的上限。

虽然@Tophandour 的回答给出了一个很好的宽度近似值,但 stb 提供了更准确地执行此操作的函数。这是我现在在 Java:

中使用的解决方案
public float getWidth(String text) {
    float length = 0f;
    for (int i = 0; i < text.length(); i++) {
        IntBuffer advancewidth = BufferUtils.createIntBuffer(1);
        IntBuffer leftsidebearing = BufferUtils.createIntBuffer(1);
        stbtt_GetCodepointHMetrics(info, (int)text.charAt(i), advancewidth, leftsidebearing);
        length += advancewidth.get(0);
    }
    return length * cscale;
}

其中 info 是通过 stbtt_InitFont 创建的字体信息对象,cscale 来自 stbtt_ScaleForPixelHeight.

LWJGL STB True Type 演示现在包含一个实现(自 2017 年 8 月起),包括字距调整:

lwjgl3 / Truetype.java:

private float getStringWidth(STBTTFontinfo info, String text, int from, int to, int fontHeight) {
    int width = 0;

    try (MemoryStack stack = stackPush()) {
        IntBuffer pCodePoint       = stack.mallocInt(1);
        IntBuffer pAdvancedWidth   = stack.mallocInt(1);
        IntBuffer pLeftSideBearing = stack.mallocInt(1);

        int i = from;
        while (i < to) {
            i += getCP(text, to, i, pCodePoint);
            int cp = pCodePoint.get(0);

            stbtt_GetCodepointHMetrics(info, cp, pAdvancedWidth, pLeftSideBearing);
            width += pAdvancedWidth.get(0);

            if (isKerningEnabled() && i < to) {
                getCP(text, to, i, pCodePoint);
                width += stbtt_GetCodepointKernAdvance(info, cp, pCodePoint.get(0));
            }
        }
    }

    return width * stbtt_ScaleForPixelHeight(info, fontHeight);
}

private static int getCP(String text, int to, int i, IntBuffer cpOut) {
    char c1 = text.charAt(i);
    if (Character.isHighSurrogate(c1) && i + 1 < to) {
        char c2 = text.charAt(i + 1);
        if (Character.isLowSurrogate(c2)) {
            cpOut.put(0, Character.toCodePoint(c1, c2));
            return 2;
        }
    }
    cpOut.put(0, c1);
    return 1;
}