将单色的FreeType位图转换为8位像素数组

3
我花了几个小时来尝试将由FreeType生成的单色位图转换为每像素使用8位的像素数组(以便我可以将它们用作OpenGL的alpha值)。我知道我可以只得到普通的8bpp位图并使用它,但我真的需要单色版本。我已经尝试过阈值化像素的方法,但结果并不令人满意。似乎一旦你有了抗锯齿版本,就无法回去了。
下面的代码片段是我现在拥有的,它非常接近完美,只有几个字母有问题。
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO))
  throw exception();

if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))
  throw exception();

const Size bitmapSize = Size(
    face->glyph->metrics.width / 64, face->glyph->metrics.height / 64
);

u8 *bitmapBits = face->glyph->bitmap.buffer;    
u8 *bitmapBytes = new u8[bitmapSize.width * bitmapSize.height];

memset(bitmapBytes, 0, bitmapSize.width * bitmapSize.height);

int width = face->glyph->bitmap.width;
int height = face->glyph->bitmap.rows;

const u8 *src = bitmapBits;

for (int row = 0; row < height; ++row)
{
  u8 *dst = &bitmapBytes[row * width];

  for (int col = 0; col < width / 8; ++col)
  {
      u8 byte = src[col];

      *dst++ = ((byte & (1 << 7)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 6)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 5)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 4)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 3)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 2)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 1)) != 0) ? 255: 0;
      *dst++ = ((byte & (1 << 0)) != 0) ? 255: 0;
  }

  u8 trailingByte = src[width / 8];

  int i = 7;

  switch (width % 8)
  {
  case 7: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 6: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 5: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 4: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 3: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 2: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 1: *dst++ = ((trailingByte & (1 << (i--))) != 0) ? 255 : 0;
  case 0: break;
  }

  src += face->glyph->bitmap.pitch;
}

这是输出结果:
我试图找出扭曲字母的共同点(也许这可以帮助我),但我不知道。
实际上,我不知道我做这件事情的方式有什么问题。但肯定有些东西我漏了。更改字体不能解决问题(除了扭曲不同的字母)。
编辑:我注意到face->glyph->bitmap.width和face->glyph->bitmap.rows有时会返回与face->glyph->metrics.width / 64和face->glyph->metrics.height / 64稍微不同的值。我尝试交换它们,渲染肯定更好,但仍然有一些字符(例如Arial的't'和'g',以及Microsoft Sans Serif的'X'和'0')有点偏移或扭曲。
Arial: https://istack.dev59.com/1DJnf.webp Microsoft Sans Serif: https://istack.dev59.com/027hs.webp 这真的很奇怪,我必须漏掉了什么,因为我不相信这是一个bug。FreeType似乎是一个非常可靠的库。此外,将字体加载为非单色工作得非常完美,因此这不是呈现字符串的代码的问题。

你可以使用FT_RENDER_MODE_NORMAL来获取一个适用于作为alpha通道的8位灰度位图,或者使用FT_Bitmap_Convert从单色位图转换为8位。你也可以查看示例代码 - Retired Ninja
@RetiredNinja 问题是,我需要具有单色位图(无反锯齿)的图像,因此FT_RENDER_MODE_NORMAL不可行(我尝试使用FT_Bitmap_Convert,但无论我做什么,都得到了乱码)。而且我不能只加载普通位图,然后通过阈值处理像素手动将其转换为单色图像,因为结果很丑陋,不如单色版本清晰和漂亮。一旦你有了抗锯齿版本,就无法回头了。 :/ - JulSe
有趣的是,即使我将FreeType的原始单色位图(使用glTexImage2D和GL_BITMAP)输入OpenGL中,它仍然无法正常工作(只得到黑色矩形而不是字母),因此似乎需要进行一些处理,我几乎做到了,除了我遇到麻烦的几个字母。有什么想法可能出了问题? - JulSe
1个回答

0

一行中的字节数不一定是适合该行位数的最小字节数,而是在位图的pitch字段中报告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接