win32 drawing text
This commit is contained in:
156
src/render/font.c
Normal file
156
src/render/font.c
Normal file
@@ -0,0 +1,156 @@
|
||||
typedef struct rn_glyph_t rn_glyph_t;
|
||||
struct rn_glyph_t {
|
||||
v2f32_t size;
|
||||
v2f32_t offset;
|
||||
f32 xadvance;
|
||||
f32 left_side_bearing;
|
||||
r2f32_t atlas_bounding_box;
|
||||
};
|
||||
|
||||
typedef struct rn_font_t rn_font_t;
|
||||
struct rn_font_t {
|
||||
rn_glyph_t *glyphs;
|
||||
i32 glyph_count;
|
||||
|
||||
u32 first_char, last_char;
|
||||
u32 texture_id;
|
||||
|
||||
f32 size;
|
||||
f32 descent;
|
||||
f32 ascent;
|
||||
f32 line_gap;
|
||||
|
||||
r2f32_t white_texture_bounding_box;
|
||||
};
|
||||
|
||||
typedef struct rn_atlas_t rn_atlas_t;
|
||||
struct rn_atlas_t {
|
||||
u8 *bitmap;
|
||||
v2i32_t size;
|
||||
v2f32_t inverse_size;
|
||||
i32 xcursor;
|
||||
i32 ycursor;
|
||||
|
||||
i32 biggest_height;
|
||||
r2f32_t white_texture_bounding_box;
|
||||
u32 texture_id;
|
||||
};
|
||||
|
||||
rn_atlas_t *rn_create_atlas(ma_arena_t *arena, v2i32_t size) {
|
||||
rn_atlas_t *result = ma_push_type(arena, rn_atlas_t);
|
||||
result->size = size;
|
||||
result->inverse_size.x = 1.f / (f32)result->size.x;
|
||||
result->inverse_size.y = 1.f / (f32)result->size.y;
|
||||
result->bitmap = ma_push_array(arena, u8, size.x * size.y);
|
||||
|
||||
// Add a whitebox first for rectangle rendering
|
||||
for (i32 y = 0; y < 16; y++) {
|
||||
for (i32 x = 0; x < 16; x++) {
|
||||
u8 *dst = result->bitmap + x + y * result->size.x;
|
||||
*dst = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
result->white_texture_bounding_box = (r2f32_t){
|
||||
.min = { 2.f * result->inverse_size.x, 2.f / (f32)result->size.y},
|
||||
.max = {14.f * result->inverse_size.x, 14.f / (f32)result->size.y}
|
||||
};
|
||||
result->xcursor += 16;
|
||||
result->biggest_height += 16;
|
||||
return result;
|
||||
}
|
||||
|
||||
r2f32_t rn_pack_bitmap(rn_atlas_t *atlas, u8 *bitmap, i32 width, i32 height) {
|
||||
// Packing into a texture atlas
|
||||
// @Inefficient The algorithm is a simplest thing I had in mind, first we advance
|
||||
// through the atlas in X packing consecutive glyphs. After we get to the end of the row
|
||||
// we advance to the next row by the Y size of the biggest packed glyph. If we get to the
|
||||
// end of atlas and fail to pack everything the app panics.
|
||||
|
||||
i32 spacing = 4;
|
||||
if (atlas->xcursor + width > atlas->size.x) {
|
||||
if (atlas->ycursor + height < atlas->size.y) {
|
||||
atlas->xcursor = 0;
|
||||
atlas->ycursor += atlas->biggest_height + spacing;
|
||||
} else {
|
||||
fatalf("error while packing a font into atlas. rn_atlas_t size for this font scale is a bit too small");
|
||||
}
|
||||
}
|
||||
|
||||
u8 *src = bitmap;
|
||||
for (i32 y = atlas->ycursor; y < atlas->ycursor + height; y += 1) {
|
||||
for (i32 x = atlas->xcursor; x < atlas->xcursor + width; x += 1) {
|
||||
u8 *dst = atlas->bitmap + x + y * atlas->size.x;
|
||||
*dst = *src++;
|
||||
}
|
||||
}
|
||||
|
||||
v2f32_t size = {(f32)width * atlas->inverse_size.x, (f32)height * atlas->inverse_size.y};
|
||||
v2f32_t pos = {(f32)atlas->xcursor * atlas->inverse_size.x, (f32)atlas->ycursor * atlas->inverse_size.y};
|
||||
r2f32_t result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y};
|
||||
|
||||
atlas->xcursor += width + spacing;
|
||||
if (height > atlas->biggest_height) {
|
||||
atlas->biggest_height = height;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
rn_font_t rn_create_font(ma_arena_t *glyph_arena, s8_t font_data, rn_atlas_t *atlas, i32 size) {
|
||||
rn_font_t result = {};
|
||||
stbtt_fontinfo stb_font;
|
||||
i32 success = stbtt_InitFont(&stb_font, (const unsigned char *)font_data.str, 0);
|
||||
if (!success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
f32 scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)size);
|
||||
// f32 em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, (f32)size);
|
||||
|
||||
i32 ascent, descent, line_gap;
|
||||
stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &line_gap);
|
||||
result.ascent = (f32)ascent * scale;
|
||||
result.descent = (f32)descent * scale;
|
||||
result.line_gap = (f32)line_gap * scale;
|
||||
result.size = (f32)size;
|
||||
result.first_char = ' ';
|
||||
result.last_char = '~';
|
||||
result.white_texture_bounding_box = atlas->white_texture_bounding_box;
|
||||
|
||||
i32 glyph_count = result.last_char - result.first_char + 1; // + 1 because it's '<=' not '<'
|
||||
result.glyphs = ma_push_array(glyph_arena, rn_glyph_t, glyph_count);
|
||||
|
||||
for (u32 ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) {
|
||||
i32 width, height, xoff, yoff;
|
||||
u8 *bitmap = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, scale, ascii_symbol, &width, &height, &xoff, &yoff);
|
||||
|
||||
i32 xadvance, left_side_bearing;
|
||||
stbtt_GetCodepointHMetrics(&stb_font, ascii_symbol, &xadvance, &left_side_bearing);
|
||||
|
||||
rn_glyph_t glyph = {};
|
||||
glyph.atlas_bounding_box = rn_pack_bitmap(atlas, bitmap, width, height);
|
||||
glyph.size = (v2f32_t){(f32)width, (f32)height};
|
||||
glyph.offset = (v2f32_t){(f32)xoff, (f32)yoff};
|
||||
glyph.xadvance = (f32)xadvance * scale;
|
||||
glyph.left_side_bearing = (f32)left_side_bearing * scale;
|
||||
|
||||
assert(result.glyph_count + 1 <= glyph_count);
|
||||
result.glyphs[result.glyph_count++] = glyph;
|
||||
|
||||
stbtt_FreeBitmap(bitmap, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
rn_glyph_t *rn_get_glyph(rn_font_t *font, u32 codepoint) {
|
||||
b32 is_in_range = codepoint >= font->first_char && codepoint <= font->last_char;
|
||||
if (is_in_range) {
|
||||
u32 index = codepoint - font->first_char;
|
||||
return &font->glyphs[index];
|
||||
} else {
|
||||
u32 index = '?' - font->first_char;
|
||||
return &font->glyphs[index];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user