То, что вы хотите сделать, не так просто. Первое неправильное понимание состоит в том, что FT_Glyph_To_Bitmap
не создаст 2 канала, один для контуров штрихов и включенный для заполненного тела, как вы и ожидали.
Вы должны создать 2 растровых изображения из глифа и объединить их вручную в одно растровое изображение с 2 каналами.
Проблема в том, что растровые изображения даже не будут иметь одинаковый размер. «Контур» растрового изображения будет немного выше, чем «заполненный».
Сначала создайте контурное растровое изображение:
FT_Error err_code = FT_Load_Char( face, i, FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_NORMAL );
if ( err_code != 0 )
{
// error handling
}
FT_Glyph glyphDescStroke;
err_code = FT_Get_Glyph( face->glyph, &glyphDescStroke );
static double outlineThickness = 2.0;
FT_Stroker_Set( stroker, static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 );
if ( err_code == 0 )
err_code = FT_Glyph_Stroke( &glyphDescStroke, stroker, true );
if ( err_code == 0 )
err_code = FT_Glyph_To_Bitmap( &glyphDescStroke, FT_RENDER_MODE_NORMAL, 0, 1);
FT_BitmapGlyph glyph_bitmap;
FT_Bitmap *bitmap_stroke = nullptr;
if ( err_code == 0 )
{
glyph_bitmap = (FT_BitmapGlyph)glyphDescStroke;
bitmap_stroke = &glyph_bitmap->bitmap;
}
Создайте временный буфер с 2 цветными каналами и скопируйте растровое изображение схемы во 2-й цветовой канал буфера:
#include <vector>
unsigned int cx = 0, cy = 0, ox = 0, oy = 0;
std::vector<unsigned char> buffer
if ( error_code == 0 && bitmap_stroke )
{
cx = bitmap_stroke->width;
cy = bitmap_stroke->rows;
ox = glyph_bitmap->left;
oy = glyph_bitmap->top;
buffer = std::vector<unsigned char>(cx * cy * 2, 0); // * 2 -> 2 color channels (red and green)
for ( unsigned int i = 0; i < cx * cy; ++ i)
buffer[i*2 + 1] = bitmap_stroke->buffer[i]; // + 1 -> 2nd color channel
}
FT_Done_Glyph( glyphDescStroke );
Создание «заполненного» растрового изображения:
FT_Glyph glyphDescFill;
err_code = FT_Get_Glyph( face->glyph, &glyphDescFill );
if ( err_code == 0 )
err_code = FT_Glyph_To_Bitmap( &glyphDescFill, FT_RENDER_MODE_NORMAL, 0, 1);
FT_Bitmap *bitmap_fill = nullptr;
if ( err_code == 0 )
{
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)glyphDescFill;
bitmap_fill = &glyph_bitmap->bitmap;
}
Добавить «заполненное» растровое изображение в 1-й канал цвета буфера:
if ( err_code == 0 && bitmap_fill )
{
unsigned int cx_fill = bitmap_fill->width;
unsigned int cy_fill = bitmap_fill->rows;
unsigned int offset_x = (cx - cx_fill) / 2; // offset because the bitmap my be smaller,
unsigned int offset_y = (cy - cy_fill) / 2; // then the former
for ( unsigned int y = 0; y < cy_fill; ++ y )
{
for ( unsigned int x = 0; x < cx_fill; ++ x )
{
unsigned int i_source = y * cx_fill + x;
unsigned int i_target = (y + offset_y) * cx + x + offset_x;
buffer[i_target*2 + 0] = bitmap_fill->buffer[i_source]; // + 0 -> 1st color channel
}
}
}
FT_Done_Glyph( glyphDescFill );
Когда вы загружаете буфер в объект текстуры, вы должны установить параметр GL_UNPACK_ALIGNMENT
, потому что битовая карта глифа buffer
плотно упакована и строка битовой карты может быть не выровнена до 4 байтов. См. glPixelStore
.
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStore( GL_UNPACK_ALIGNMENT, 1 ); // of course 2 will work too (2 channels)
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RG8, // 2 channels each with 8 bits
cx,
cy,
GL_RG, // "GL_RG" - "GL_RG16" is not a valid format
GL_UNSIGNED_BYTE,
buffer.data() );
glPixelStore( GL_UNPACK_ALIGNMENT, 4 );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Добавить символьные данные в структуру данных:
Character character = {
texture,
glm::ivec2(cx, cy),
glm::ivec2(ox, oy),
face->glyph->advance.x
};
Characters.insert(std::pair<GLchar, Character>(c, character));
Код GLSL, который рисует глиф с отдельным цветом для контура и заполненного интерьера, который использует функцию GLSL mix
может выглядеть следующим образом:
#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D text;
uniform vec3 textColor;
uniform vec3 outlinecolor;
void main()
{
vec3 fill_col = vec3(0.0, 0.0, 1.0); // e.g blue
vec3 outline_col = vec3(1.0, 0.0, 0.0); // e.g red
vec2 tex = texture2D(text, TexCoords).rg;
float fill = tex.r;
float outline = tex.g;
float alpha = max( fill, outline );
vec3 mix_color = mix( mix(vec3(0.0), fill_col, fill), outline_col, outline );
color = vec4(mix_color, alpha);
}
Предварительный просмотр со шрифтом pixlim (2) .ttf из репозитория, который вы указали в комментариях: