c++ - 如何使用 Freetype 沿基线对齐字形?

标签 c++ opengl fonts text-rendering freetype2

我编写了一个函数,可以使用 Freetype2 从字体创建图像。我的目标是使用字体中的所有字符创建 OpenGL 纹理。但首先,我想确保正确创建纹理,因此我现在使用 stb_image_write 将其存储为图像。这是我迄今为止的进展:

The first few letters from the image

正如您所看到的,字形不是沿着基线对齐,而是沿着图像的顶部对齐。因此,我决定引入一个名为“yOffset”的变量,它描述每个字形必须向下移动才能正确对齐的像素数量。

根据Render FreeType text with flipped ortho, difference between top and baseline of glyph中的回答,这个偏移量可以通过以下方式计算

yOffset = (Glyph with highest y-Bearing)->bitmap_top - glyph->bitmap_top

该值通过第一个循环存储在“maxAscent”中,该循环找出每个字形的度量。

这是字形度量的直观表示:

enter image description here

这是我的功能:

static void loadFontImage(const std::string& fontPath, unsigned int fontSize, const std::string& imagePath) {
        FT_Library library;
        FT_Face face;

        unsigned int imageWidth = 0;
        unsigned int imageHeight = 0;
        unsigned int maxAscent = 0;

        if (FT_Init_FreeType(&library)) {
            std::cerr << "Could not initialize font library" << std::endl;
            std::exit(-1);
        }

        if (FT_New_Face(library, fontPath.c_str(), 0, &face)) {
            std::cerr << "Could not load font '" << fontPath << "'" << std::endl;
            std::exit(-1);
        }

        FT_Set_Char_Size(face, 0, fontSize * 64, 300, 300);

        FT_GlyphSlot glyph = face->glyph;

        for (unsigned int c = 32; c < 256; c++) {
            if (c == 127) continue;
            if (FT_Load_Char(face, c, FT_LOAD_RENDER)) continue;

            imageWidth += glyph->bitmap.width;

            if (glyph->bitmap.rows > (int)imageHeight) {
                imageHeight = glyph->bitmap.rows;
            }

            if (glyph->bitmap_top > (int)maxAscent) {
                maxAscent = glyph->bitmap_top;
            }
        }

        unsigned int size = imageWidth * imageHeight;
        unsigned char* buffer = new unsigned char[size];
        unsigned int xOffset = 0;
        unsigned int yOffset = 0;

        for (unsigned int c = 32; c < 256; c++) {
            if (c == 127) continue;
            if (FT_Load_Char(face, c, FT_LOAD_RENDER)) continue;

            yOffset = maxAscent - glyph->bitmap_top;

            for (unsigned int x = 0; x < glyph->bitmap.width; x++) {
                for (unsigned int y = 0; y < glyph->bitmap.rows; y++) {
                    unsigned int imageIndex = (x + xOffset) + (y + yOffset) * imageWidth;
                    unsigned int bitmapIndex = x + y * glyph->bitmap.width;
                    buffer[imageIndex] = glyph->bitmap.buffer[bitmapIndex];
                }
            }

            xOffset += glyph->bitmap.width;
        }

        stbi_write_png(imagePath.c_str(), imageWidth, imageHeight, 1, buffer, imageWidth);

        delete[] buffer;
    }
}

但是自从我引入了 yOffset 后,“索引越界”类型的异常仅发生在某些字形上,因为字形的高度加上 yOffset 大于图像高度。

我认为这是因为我的“maxAscent”公式不正确,因此我的 yOffset 对于某些字符来说太大。因此我的问题是:这确实是正确的公式吗?如果是,我的算法还有什么问题?是否有更简单的方法来实现正确的对齐?

最佳答案

在我注意到图像的实际高度计算不正确后,我通过简单的修复解决了这个问题。这是因为“最高”字形的高度并不代表图像的实际高度。例如,一个字符可能小于最高的字形,但放置在基线上方更高的位置,因此超出了范围。

我引入了另一个名为“maxDescent”的变量,它存储基线以下的最大像素数。在第一个循环中计算如下:

if ((glyph->metrics.height >> 6) - glyph->bitmap_top > (int)maxDescent) {
    maxDescent = (glyph->metrics.height >> 6) - glyph->bitmap_top;
}

与存储基线上方最大像素数的“maxAscent”一起,我只需将它们相加即可计算出图像所需的实际高度。

imageHeight = maxAscent + maxDescent

这是结果的一部分:

Resulting Font Image

关于c++ - 如何使用 Freetype 沿基线对齐字形?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62374506/

相关文章:

opengl - 物体不会旋转?

python - 从 Python 调用 OpenGL 扩展

c++ - 在 FTGL 中正确定位字体的 y 轴

c++ - Qt-从资源中添加自定义字体

c++ - 如何为自定义模板化迭代器实现 std::distance()?

c++ - 使用 C++ 成员函数处理采用简单静态函数指针的回调

C++处理文件

c++ - 税务计算器问题

c++ - 单个 3d 立方体的 z_axis 移动

java - CentOS 6.5 + JasperReports 5.6.0 = 读取字体数据时出现异常