# 优化 stb_truetype 在 mono 模式下的字体显示效果

stb_truetype 解析 ttf 字体文件时,输出的字模为灰度图,如果需要在mono模式下使用stb_truetype 则需要另外编写二值化算法。

# 一、stb_truetype 的二值化算法

AWTK 通过给定阈值实现 stb_truetype 的二值化算法,代码如下:

//awtk/src/font_loader/font_loader_stb.c
static ret_t font_stb_gray_to_mono_by_threshold(const glyph_t* gray, glyph_t* mono,
                                                uint32_t threshold) {
  return_value_if_fail(gray != NULL && mono != NULL && gray->format == GLYPH_FMT_ALPHA,
                       RET_BAD_PARAMS);
  uint32_t i = 0;
  uint32_t j = 0;
  uint16_t h = gray->h;
  uint16_t w = gray->w;

  mono->format = GLYPH_FMT_MONO;
  mono->h = h;
  mono->w = w;
  mono->pitch = ((mono->w + 15) >> 4) << 1;
  mono->x = gray->x;
  mono->y = gray->y;
  mono->advance = gray->advance;

  uint32_t nmemb = mono->pitch * h;
  uint8_t* bitmap = TKMEM_CALLOC(nmemb, sizeof(uint8_t));
  return_value_if_fail(bitmap != NULL, RET_OOM);
  for (j = 0; j < h; ++j) {
    for (i = 0; i < w; ++i) {
      if (gray->data[w * j + i] > threshold) {
        uint32_t offset = j * mono->pitch + (i >> 3);
        uint32_t offset_bit = 7 - (i % 8);
        bitmap[offset] += 0x1 << offset_bit;
      }
    }
  }
  mono->data = bitmap;
  return RET_OK;
}

# 二、优化二值化阈值

经测试发现二值化阈值与 ttf 字体文件和字号有关,因此,对 stb_truetype 获取的灰度图字模进行二值化的阈值做了调整,在不同字号区间使用不同的阈值,代码如下:

//awtk/src/font_loader/font_loader_stb.c
static ret_t font_stb_get_glyph(font_t* f, wchar_t c, font_size_t font_size, glyph_t* g) {
  ...
  if (bitmap != NULL) {
    if (font->mono) {
      glyph_t* gray_g = glyph_clone(g);
      gray_g->data = bitmap;
      uint32_t threshold = font_size > 24? (font_size > 48 ? (font_size > 71 ? (font_size > 95 ? 195 : 175) : 160) : 118) : 95;
      font_stb_gray_to_mono_by_threshold(gray_g, g, threshold);
      STBTT_free(bitmap, NULL);
      glyph_destroy(gray_g);
    } else {
      g->data = bitmap;
    }
    ...
  }

  return g->data != NULL || c == ' ' ? RET_OK : RET_NOT_FOUND;
}

可以看出 stb_truetype 在 mono 模式下的字体显示效果主要由阈值 threshold 决定,这些阈值是通过测试不断调整得到的,用户也可针对自己使用矢量字体文件进行调整。