(freetype/stb_unicode/bitmapfont) Prevent texture bleed when rendering text at non-integer scales

This commit is contained in:
jdgleaver 2021-04-15 17:22:28 +01:00
parent b69e023736
commit fc05c0805b
4 changed files with 61 additions and 20 deletions

View File

@ -28,6 +28,11 @@
#define BMP_ATLAS_ROWS 16
#define BMP_ATLAS_SIZE (BMP_ATLAS_COLS * BMP_ATLAS_ROWS)
/* Padding is required between each glyph in
* the atlas to prevent texture bleed when
* drawing with linear filtering enabled */
#define BMP_ATLAS_PADDING 1
typedef struct bm_renderer
{
unsigned scale_factor;
@ -96,16 +101,16 @@ static void *font_renderer_bmp_init(const char *font_path, float font_size)
if (!handle->scale_factor)
handle->scale_factor = 1;
handle->atlas.width = FONT_WIDTH * handle->scale_factor * BMP_ATLAS_COLS;
handle->atlas.height = FONT_HEIGHT * handle->scale_factor * BMP_ATLAS_ROWS;
handle->atlas.width = (BMP_ATLAS_PADDING + (FONT_WIDTH * handle->scale_factor)) * BMP_ATLAS_COLS;
handle->atlas.height = (BMP_ATLAS_PADDING + (FONT_HEIGHT * handle->scale_factor)) * BMP_ATLAS_ROWS;
handle->atlas.buffer = (uint8_t*)calloc(handle->atlas.width * handle->atlas.height, 1);
for (i = 0; i < BMP_ATLAS_SIZE; i++)
{
unsigned x = (i % BMP_ATLAS_COLS) *
handle->scale_factor * FONT_WIDTH;
(BMP_ATLAS_PADDING + (handle->scale_factor * FONT_WIDTH));
unsigned y = (i / BMP_ATLAS_COLS) *
handle->scale_factor * FONT_HEIGHT;
(BMP_ATLAS_PADDING + (handle->scale_factor * FONT_HEIGHT));
char_to_texture(handle, i, x, y);

View File

@ -48,6 +48,10 @@
#define FT_ATLAS_ROWS 16
#define FT_ATLAS_COLS 16
#define FT_ATLAS_SIZE (FT_ATLAS_ROWS * FT_ATLAS_COLS)
/* Padding is required between each glyph in
* the atlas to prevent texture bleed when
* drawing with linear filtering enabled */
#define FT_ATLAS_PADDING 1
typedef struct freetype_atlas_slot
{
@ -64,6 +68,8 @@ typedef struct freetype_renderer
struct font_atlas atlas; /* ptr alignment */
freetype_atlas_slot_t atlas_slots[FT_ATLAS_SIZE]; /* ptr alignment */
freetype_atlas_slot_t* uc_map[0x100]; /* ptr alignment */
unsigned max_glyph_width;
unsigned max_glyph_height;
unsigned usage_counter;
struct font_line_metrics line_metrics; /* float alignment */
} ft_font_renderer_t;
@ -165,13 +171,35 @@ static const struct font_glyph *font_renderer_ft_get_glyph(
if (slot->bitmap.buffer)
{
unsigned r, c;
const uint8_t *src = (const uint8_t*)slot->bitmap.buffer;
const uint8_t *src = (const uint8_t*)slot->bitmap.buffer;
unsigned delta_width = (handle->max_glyph_width > atlas_slot->glyph.width) ?
(handle->max_glyph_width - atlas_slot->glyph.width) : 0;
unsigned x, y;
for (r = 0; r < atlas_slot->glyph.height;
r++, dst += handle->atlas.width, src += slot->bitmap.pitch)
for (c = 0; c < atlas_slot->glyph.width; c++)
dst[c] = src[c];
/* When copying the glyph bitmap, it is
* necessary to clear any unused regions of
* the atlas texture, otherwise garbage
* (due to texture bleeding) may be drawn at
* the edges of the glyph when rendering with
* filtering enabled */
for (y = 0; y < atlas_slot->glyph.height; y++)
{
/* Copy bitmap row */
memcpy(dst, src, atlas_slot->glyph.width * sizeof(uint8_t));
/* Zero out remaining atlas row */
memset(dst + atlas_slot->glyph.width, 0, delta_width * sizeof(uint8_t));
dst += handle->atlas.width;
src += slot->bitmap.pitch;
}
/* Zero out unused atlas rows */
for (y = atlas_slot->glyph.height; y < handle->max_glyph_height; y++)
{
memset(dst, 0, handle->max_glyph_width * sizeof(uint8_t));
dst += handle->atlas.width;
}
}
handle->atlas.dirty = true;
@ -184,12 +212,11 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle, float font_si
unsigned i, x, y;
freetype_atlas_slot_t* slot = NULL;
unsigned max_width = round((handle->face->bbox.xMax - handle->face->bbox.xMin) * font_size / handle->face->units_per_EM);
unsigned max_width = round((handle->face->bbox.xMax - handle->face->bbox.xMin) * font_size / handle->face->units_per_EM);
unsigned max_height = round((handle->face->bbox.yMax - handle->face->bbox.yMin) * font_size / handle->face->units_per_EM);
unsigned atlas_width = max_width * FT_ATLAS_COLS;
unsigned atlas_height = max_height * FT_ATLAS_ROWS;
unsigned atlas_width = (max_width + FT_ATLAS_PADDING) * FT_ATLAS_COLS;
unsigned atlas_height = (max_height + FT_ATLAS_PADDING) * FT_ATLAS_ROWS;
uint8_t *atlas_buffer = (uint8_t*)
calloc(atlas_width * atlas_height, 1);
@ -197,6 +224,8 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle, float font_si
if (!atlas_buffer)
return false;
handle->max_glyph_width = max_width;
handle->max_glyph_height = max_height;
handle->atlas.buffer = atlas_buffer;
handle->atlas.width = atlas_width;
handle->atlas.height = atlas_height;
@ -206,8 +235,8 @@ static bool font_renderer_create_atlas(ft_font_renderer_t *handle, float font_si
{
for (x = 0; x < FT_ATLAS_COLS; x++)
{
slot->glyph.atlas_offset_x = x * max_width;
slot->glyph.atlas_offset_y = y * max_height;
slot->glyph.atlas_offset_x = x * (max_width + FT_ATLAS_PADDING);
slot->glyph.atlas_offset_y = y * (max_height + FT_ATLAS_PADDING);
slot++;
}
}

View File

@ -86,6 +86,9 @@ static bool font_renderer_stb_create_atlas(stb_font_renderer_t *self,
if (!self->atlas.buffer)
goto error;
/* Note: 1 pixel of padding is added to
* prevent texture bleed when drawing with
* linear filtering enabled */
stbtt_PackBegin(&pc, self->atlas.buffer,
self->atlas.width, self->atlas.height,
self->atlas.width, 1, NULL);

View File

@ -42,6 +42,10 @@
#define STB_UNICODE_ATLAS_ROWS 16
#define STB_UNICODE_ATLAS_COLS 16
#define STB_UNICODE_ATLAS_SIZE (STB_UNICODE_ATLAS_ROWS * STB_UNICODE_ATLAS_COLS)
/* Padding is required between each glyph in
* the atlas to prevent texture bleed when
* drawing with linear filtering enabled */
#define STB_UNICODE_ATLAS_PADDING 1
typedef struct stb_unicode_atlas_slot
{
@ -195,8 +199,8 @@ static bool font_renderer_stb_unicode_create_atlas(
self->max_glyph_width = font_size < 0 ? -font_size : font_size;
self->max_glyph_height = font_size < 0 ? -font_size : font_size;
self->atlas.width = self->max_glyph_width * STB_UNICODE_ATLAS_COLS;
self->atlas.height = self->max_glyph_height * STB_UNICODE_ATLAS_ROWS;
self->atlas.width = (self->max_glyph_width + STB_UNICODE_ATLAS_PADDING) * STB_UNICODE_ATLAS_COLS;
self->atlas.height = (self->max_glyph_height + STB_UNICODE_ATLAS_PADDING) * STB_UNICODE_ATLAS_ROWS;
self->atlas.buffer = (uint8_t*)
calloc(self->atlas.width * self->atlas.height, sizeof(uint8_t));
@ -210,8 +214,8 @@ static bool font_renderer_stb_unicode_create_atlas(
{
for (x = 0; x < STB_UNICODE_ATLAS_COLS; x++)
{
slot->glyph.atlas_offset_x = x * self->max_glyph_width;
slot->glyph.atlas_offset_y = y * self->max_glyph_height;
slot->glyph.atlas_offset_x = x * (self->max_glyph_width + STB_UNICODE_ATLAS_PADDING);
slot->glyph.atlas_offset_y = y * (self->max_glyph_height + STB_UNICODE_ATLAS_PADDING);
slot++;
}
}