【问题标题】:stb_truetype calculating the dimensions of a stringstb_truetype 计算字符串的维度
【发布时间】:2016-12-31 23:05:31
【问题描述】:

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

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

【问题讨论】:

  • 这和OpenGL有什么关系?高度/宽度计算是您使用的任何字体库的函数。
  • 只是因为我无法获得 stb 的标签
  • @Briggy 你有没有找到解决方案?如果是这样,请发布它。如果不是,您使用的是什么语言?我在 LWJGL 3 与 stb(Java)的绑定中找到了一种方法,但我想在发布之前确保我的答案是相关的。
  • @Tophandour 我也在使用 Java,但这应该与所有具有 stb 绑定的语言相关。我最终将一行中每个字符的 stbtt_GetCodepointHMetrics 值相加,然后按 stbtt_ScaleForPixelHeight 进行缩放。
  • @Briggy 我能够通过不同的(尽管希望是有效的)手段做到这一点。回家后我会发布我的解决方案。

标签: opengl truetype


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我能够通过 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,同时保持first_char,这是一个int

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

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

    【讨论】:

    • 这个方法是最初使用的方法,但没有给你正确的值。这是一个很好的近似值
    • @Briggy 现在在我的代码中,我通过获取字体的“M”字符的宽度并将其乘以字符串长度来近似长度,因为我需要快速得到一些工作。我注意到获取空格字符的宽度会得到 0 的结果,如果您迭代,这是一个问题。我会更新我的答案。
    【解决方案3】:

    虽然@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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 2023-03-18
      • 1970-01-01
      相关资源
      最近更新 更多