Преобразование монохромного растрового изображения FreeType в 8-битный массив пикселей

Я потратил несколько часов, пытаясь преобразовать монохромное растровое изображение, созданное FreeType, в массив пикселей, который использует 8 бит на пиксель (чтобы я мог использовать их в качестве альфа-значений для OpenGL). Я знаю, что могу просто взять обычное растровое изображение 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. Я пробовал их переключать, и рендеринг определенно стал лучше, но все еще есть некоторые символы (например, «t» и «g» в Arial и «X» и «0» в Microsoft Sans Serif), которые немного сдвинуты или искажены.

Arial: введите здесь описание изображения

Microsoft Sans Serif: введите здесь описание изображения

Это действительно странно, и я должен что-то упустить, потому что я не верю, что это ошибка. FreeType кажется слишком солидной и надежной библиотекой. Кроме того, загрузка шрифтов как немонохроматических работает отлично, так что это не проблема с кодом, который отображает строку.


person JulSe    schedule 28.08.2018    source источник
comment
Вы можете использовать FT_RENDER_MODE_NORMAL, чтобы получить 8-битное растровое изображение в градациях серого, подходящее для использования в качестве альфа-канала, или FT_Bitmap_Convert, чтобы преобразовать монохромное растровое изображение в 8-битное. Вы также можете ознакомиться с примером кода.   -  person Retired Ninja    schedule 29.08.2018
comment
@RetiredNinja Дело в том, что мне нужно иметь монохромное растровое изображение (без сглаживания), поэтому FT_RENDER_MODE_NORMAL не может быть и речи (я пытался использовать FT_Bitmap_Convert, но что бы я ни делал, я получил тарабарщину). И я не могу просто загрузить обычное растровое изображение, а затем вручную преобразовать его в монохромное изображение, установив пороговое значение для пикселей, потому что результат получается уродливым и не таким четким и приятным, как монохромная версия. Вы просто не можете повернуть назад, если у вас есть версия со сглаживанием. :/   -  person JulSe    schedule 29.08.2018
comment
Самое смешное, что даже когда я загружаю необработанный монохромный битмап из FreeType в OpenGL (с glTexImage2D и GL_BITMAP), он вообще не работает (только черные прямоугольники вместо букв), так что, похоже, должна быть какая-то обработка участвовал, и я почти понял, за исключением нескольких букв, с которыми у меня возникли проблемы. Любые идеи, что может быть не так?   -  person JulSe    schedule 29.08.2018


Ответы (1)


Количество байтов в строке не обязательно является минимальным числом, которое соответствует битам в строке, но сообщается в поле pitch растрового изображения.

person Paolo Capriotti    schedule 11.10.2019