summaryrefslogtreecommitdiff
path: root/code
diff options
context:
space:
mode:
authortalha <talha@talhaamir.xyz>2023-09-02 22:10:39 +0500
committertalha <talha@talhaamir.xyz>2023-09-02 22:10:39 +0500
commitb718dead6ffdef7df5836b7d9b112b4f38e82378 (patch)
treefeb5cd34cfd21199799a5864d7b9ba11d03c8200 /code
parentdf0d5fcea9682c7890f3b0ae5e8caea2a3867199 (diff)
Adding basic font rendering functions:HEADmain
- moving existing code into separate function calls to make it easier
Diffstat (limited to 'code')
-rw-r--r--code/amr_fonts.h156
-rw-r--r--code/amr_memory.h2
-rw-r--r--code/win32_main.cpp127
3 files changed, 176 insertions, 109 deletions
diff --git a/code/amr_fonts.h b/code/amr_fonts.h
index f44893c..81666a0 100644
--- a/code/amr_fonts.h
+++ b/code/amr_fonts.h
@@ -1,25 +1,46 @@
#ifndef AMR_FONTS_H
#define AMR_FONTS_H
-typedef struct amr_glyph_info {
- u32 charset_start_index;
- u32 charset_end_index;
-} amr_glyph_info;
+#define LATIN_BASIC_START_INDEX 32
+#define LATIN_BASIC_END_INDEX 126
-typedef struct amr_font_details_debug {
+typedef struct amr_letter_details_debug {
i32 advance;
i32 lsb;
i32 x0, y0, x1, y1;
- i32 kern;
- f32 lx;
- f32 ly;
i32 byte_offset;
- i32 baseline;
-} debug_font_details;
+ // @TODO: check if offsets are in PX
+ f32 StartingYOffset;
+ f32 StartingXOffset;
+} amr_letter_details_debug;
typedef struct amr_font_state_debug {
stbtt_fontinfo font_info;
+ amr_letter_details_debug font_details;
+ // font metrics
+ f32 font_size;
+ f32 scale_factor;
+ i32 ascent;
+ i32 descent;
+ i32 line_gap;
+ // glyphs
+ u32 charset_start_index;
+ u32 charset_end_index;
+
+ // @TODO: make this array
+ u32 *glyph_index_array;
+ u32 glyph_count;
+
+ // text specific metrics
+ // @TODO: these should probably be moved to an array or remain out of this struct
+ f32 Baseline;
+ f32 MaxLineWidthPx;
+ // font bitmap info
+ unsigned char *FontBitmap;
+ u32 FontBitmapWidth;
+ u32 FontBitmapHeight;
} amr_font_state;
+
// initialise a font and loads the font in the fontbuffer
// input:
// - font path
@@ -28,7 +49,16 @@ typedef struct amr_font_state_debug {
// output:
// - error code
u32 amr_InitFont(const u8 *FontPath, u8 *FontBuffer, u32 FontFz);
-u32 amr_GetGlyphIdFromCodepoint(amr_glyph_info glyph, u32 *GlyphIndexArray, u32 Codepoint);
+void amr_LoadFontMetrics(amr_font_state font, u32 FontSize);
+void amr_LoadGlyphsFromFont(amr_DebugArena *MemoryArena, amr_font_state *font);
+u32 amr_GetGlyphIdFromCodepoint(amr_font_state font, u32 Codepoint);
+
+//////////////////////////////////////////////////////////////////////////////
+/// Naive Font Bitmap generation
+/// This approach is fast but usese more memory
+/// We make a single draw call, but load the entire text in a single bitmap
+//////////////////////////////////////////////////////////////////////////////
+void amr_MakeFontBitmapNaive(GameState *State, amr_font_state *Font, char *Text, u32 TextSize);
u32 amr_InitFont(stbtt_fontinfo *font_info, const char *FontPath, u8 *FontBuffer, u32 FontSz)
{
@@ -42,10 +72,110 @@ u32 amr_InitFont(stbtt_fontinfo *font_info, const char *FontPath, u8 *FontBuffer
return 0;
}
-u32 amr_GetGlyphIdFromCodepoint(amr_glyph_info glyph, u32 *GlyphIndexArray, u32 Codepoint)
+void amr_LoadFontMetrics(amr_font_state *Font, f32 FontSize)
+{
+ // get a SF (scale factor)
+ Font->scale_factor = stbtt_ScaleForPixelHeight(&(Font->font_info), FontSize);
+
+ // get vertical metrics
+ stbtt_GetFontVMetrics(&(Font->font_info), &(Font->ascent),
+ &(Font->descent), &(Font->line_gap));
+}
+
+void amr_LoadGlyphsFromFont(amr_DebugArena *MemoryArena, amr_font_state *Font)
+{
+ Font->glyph_count = Font->charset_end_index - Font->charset_start_index;
+ Font->glyph_index_array = (u32 *)amr_ArenaAlloc(MemoryArena,
+ sizeof(Font->glyph_count) * Font->glyph_count);
+
+ u32 *array_iter = Font->glyph_index_array;
+ for (u32 i = 0; i < Font->glyph_count; i++)
+ {
+ u32 offset_codepoint = Font->charset_start_index + i;
+ *array_iter = stbtt_FindGlyphIndex(&(Font->font_info), offset_codepoint);
+ array_iter++;
+ }
+}
+
+u32 amr_GetGlyphIdFromCodepoint(amr_font_state Font, u32 Codepoint)
{
- u32 glyph_id = *(GlyphIndexArray + (Codepoint - glyph.charset_start_index));
+ u32 glyph_id = *(Font.glyph_index_array + (Codepoint - Font.charset_start_index));
return glyph_id;
}
+void amr_MakeFontBitmapNaive(GameState *State, amr_font_state *Font, char *Text, u32 TextSize)
+{
+ Font->MaxLineWidthPx = 0.0f;
+ Font->Baseline = roundf(Font->ascent * Font->scale_factor);
+
+ u32 fd_sz = sizeof(amr_letter_details_debug)*TextSize;
+ amr_letter_details_debug *fd_arr = (amr_letter_details_debug *)amr_ArenaAlloc(&(State->Memory.Arena), fd_sz);
+ amr_letter_details_debug *_fi = fd_arr;
+
+ f32 RunningXOffset = 0.0f;
+ // convert str to font
+ for (i32 i = 0; i < TextSize; ++i)
+ {
+ _fi->StartingXOffset = RunningXOffset;
+ if (Text[i] == '\n')
+ {
+ // if new line, move baseline down
+ Font->Baseline += (i32)roundf((Font->ascent - Font->descent
+ + Font->line_gap) * Font->scale_factor);
+ RunningXOffset = 0;
+ ++_fi;
+ continue;
+ }
+
+ u32 GlyphId = amr_GetGlyphIdFromCodepoint(*Font, Text[i]);
+ stbtt_GetGlyphHMetrics(&(Font->font_info), GlyphId,
+ &_fi->advance, &_fi->lsb);
+ stbtt_GetGlyphBitmapBox(&(Font->font_info), GlyphId,
+ Font->scale_factor, Font->scale_factor,
+ &_fi->x0, &_fi->y0, &_fi->x1, &_fi->y1);
+
+ _fi->StartingYOffset = (f32)(Font->Baseline + _fi->y0);
+ RunningXOffset += (_fi->advance * Font->scale_factor);
+
+ if (i < TextSize - 1 && Text[i+1] != '\n')
+ {
+ i32 kern = stbtt_GetGlyphKernAdvance(&(Font->font_info),
+ GlyphId, amr_GetGlyphIdFromCodepoint(*Font, Text[i+1]));
+ RunningXOffset += roundf(kern * Font->scale_factor);
+ }
+
+ if (RunningXOffset > Font->MaxLineWidthPx)
+ {
+ Font->MaxLineWidthPx = RunningXOffset;
+ }
+ ++_fi;
+ }
+
+ // make all the glyphs into bitmaps.
+ _fi = fd_arr;
+ Font->FontBitmapWidth = (u32)Font->MaxLineWidthPx;
+ Font->FontBitmapHeight = (u32)(Font->Baseline -
+ roundf(Font->scale_factor * Font->descent));
+
+ u32 bitmap_size = sizeof(unsigned char)*Font->FontBitmapWidth*Font->FontBitmapHeight;
+ Font->FontBitmap = (unsigned char *)amr_ArenaAlloc(&(State->Memory.Arena), bitmap_size);
+
+ _fi = fd_arr;
+ for (int i = 0; i < TextSize; i++)
+ {
+ if (Text[i] == '\n') {
+ ++_fi;
+ continue;
+ }
+ _fi->byte_offset = (i32)(_fi->StartingXOffset + roundf(_fi->lsb * Font->scale_factor) +
+ (_fi->StartingYOffset * Font->FontBitmapWidth)) ;
+ // store image data in bitmap
+ stbtt_MakeGlyphBitmap(&(Font->font_info), Font->FontBitmap + _fi->byte_offset,
+ _fi->x1 - _fi->x0, _fi->y1 - _fi->y0, Font->FontBitmapWidth,
+ Font->scale_factor, Font->scale_factor,
+ amr_GetGlyphIdFromCodepoint(*Font, Text[i]));
+ _fi++;
+ }
+}
+
#endif
diff --git a/code/amr_memory.h b/code/amr_memory.h
index f7638b3..bc5c1ad 100644
--- a/code/amr_memory.h
+++ b/code/amr_memory.h
@@ -6,10 +6,10 @@
#endif
typedef struct amr_DebugArena {
- u8 *Buffer;
size_t Size;
size_t CurrOffset;
size_t PrevOffset;
+ u8 *Buffer;
} amr_DebugArena;
bool amr_IsPowerOfTwo(uintptr_t x);
diff --git a/code/win32_main.cpp b/code/win32_main.cpp
index 4ba86e1..2153ece 100644
--- a/code/win32_main.cpp
+++ b/code/win32_main.cpp
@@ -33,12 +33,13 @@ typedef double f64;
#define internal static
/**
- * - enhance filereading to read bytes
- * error todo:
+ * todo:
+ * - Add a stack allocator, have many cases where it would be useful
+ * - create a safe(r) way to use arrays with an array struct having a length field
+ * - will be useful for 2d arrays as well as we will have a len and height
* - start having error codes to help with error detection in case something went wrong in functions
* Font rendering todos:
- * - Look into font-packing, what is that? why is it needed?
- * - Look into generating a single bitmap and then extracting characters from the
+ * - Look into generating a font atlas and then generating quads from the
* bitmap as needed
* - SDF font rendering
* */
@@ -105,8 +106,8 @@ Win32GetSecondsElapsed(LARGE_INTEGER Start, LARGE_INTEGER End)
#include "amr_memory.c"
#include "gl_graphics.cpp"
//#include "amr_camera.c"
-#include "amr_fonts.h"
#include "game_main.h"
+#include "amr_fonts.h"
int main()
{
@@ -199,7 +200,6 @@ int main()
amr_ArenaFreeAll(&State.Memory.Arena);
// =================== FONT RENDERING ===================
- // @resume: abstracting font rendering to draw text easily
// --------- Init Font -----------------
// input:
// - filepath, memory buffer, memory buffer size
@@ -211,92 +211,29 @@ int main()
amr_InitFont(&(ArialFontState.font_info), "c:/windows/fonts/arial.ttf", ArialBuffer, font_fz);
///////////////////////////////////// PREPARE TEXT INFO ///////////////////
- // get a SF (scale factor)
- f32 SF = stbtt_ScaleForPixelHeight(&(ArialFontState.font_info), 32.0f);
-
- // get vertical metrics
- i32 f_ascent, f_descent, f_line_gap;
- stbtt_GetFontVMetrics(&(ArialFontState.font_info), &f_ascent, &f_descent, &f_line_gap);
-
- // ========================= GLYPH AND FONT INFO STORAGE ================
- amr_glyph_info latin_basic = {32, 126};
- u32 glyph_count = latin_basic.charset_end_index - latin_basic.charset_start_index;
- u32 *GlyphIndexArray = (u32 *)amr_ArenaAlloc(&State.Memory.Arena, sizeof(glyph_count) * glyph_count);
- u32 *glyph_array_iter = GlyphIndexArray;
- for (u32 i = 0; i < glyph_count; i++)
- {
- *glyph_array_iter = stbtt_FindGlyphIndex(&(ArialFontState.font_info), latin_basic.charset_start_index + i);
- glyph_array_iter++;
- }
-
- const char *text = "Hello this is a\npiece of test text \nor and more on a newline.";
- u8 char_sz = 61;
-
- u32 fd_sz = sizeof(debug_font_details)*char_sz;
-
- debug_font_details *fd_arr = (debug_font_details *)amr_ArenaAlloc(&State.Memory.Arena, fd_sz);
- debug_font_details *_fi = fd_arr;
-
- f32 lx = 0.0f, max_lx = 0.0f;
- i32 baseline = (i32)roundf(f_ascent * SF);
- // convert str to font
- for (i32 i = 0; i < char_sz; ++i)
- {
- _fi->lx = lx;
- _fi->baseline = baseline;
-
- if (text[i] == '\n')
- {
- // if new line, move baseline down
- baseline += (i32)roundf((f_ascent - f_descent + f_line_gap) * SF);
- lx = 0;
- ++_fi;
- continue;
- }
-
- stbtt_GetGlyphHMetrics(&(ArialFontState.font_info), amr_GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), &_fi->advance, &_fi->lsb);
- stbtt_GetGlyphBitmapBox(&(ArialFontState.font_info), amr_GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), SF, SF, &_fi->x0, &_fi->y0, &_fi->x1, &_fi->y1);
-
- _fi->ly = (f32)(_fi->baseline + _fi->y0);
- lx += (_fi->advance * SF);
-
- i32 kern;
- if (i < char_sz - 1 && text[i+1] != '\n')
- {
- kern = stbtt_GetGlyphKernAdvance(&(ArialFontState.font_info), amr_GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), amr_GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i+1]));
- lx += roundf(kern * SF);
- }
-
- if (lx > max_lx)
- {
- max_lx = lx;
- }
- ++_fi;
- }
-
- // make all the glyphs into bitmaps.
- _fi = fd_arr;
- u32 bitmap_width_from_text_info = (u32)max_lx;
- u32 bitmap_height_from_text_info = (u32)(baseline - roundf(SF * f_descent));
+ amr_LoadFontMetrics(&ArialFontState, 32.0f);
- i32 bmp_sz = sizeof(u8)*bitmap_width_from_text_info*bitmap_height_from_text_info;
- u8 *Bitmap = (u8 *)amr_ArenaAlloc(&State.Memory.Arena, bmp_sz);
-
- _fi = fd_arr;
- for (int i=0; i<char_sz; i++)
- {
- if (text[i] == '\n') {
- ++_fi;
- continue;
- }
- _fi->byte_offset = (i32)(_fi->lx + roundf(_fi->lsb * SF) + (_fi->ly * bitmap_width_from_text_info)) ;
- // store image data in bitmap
- stbtt_MakeGlyphBitmap(&(ArialFontState.font_info), Bitmap + _fi->byte_offset, _fi->x1 - _fi->x0, _fi->y1 - _fi->y0, bitmap_width_from_text_info, SF, SF, amr_GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]));
- _fi++;
- }
+ // ========================= GLYPH AND FONT INFO STORAGE ================
+ ArialFontState.charset_start_index = LATIN_BASIC_START_INDEX;
+ ArialFontState.charset_end_index = LATIN_BASIC_END_INDEX;
+ amr_LoadGlyphsFromFont(&State.Memory.Arena, &ArialFontState);
+
+ // ========================= NAIVE FONT BITMAP GENERATION ===============
+ // Load text into bitmaps
+ char *text = "Hello this is a\npiece of test text \nor and more on a newline.";
+ u8 text_sz = 61;
+
+ // @TODO: Add support for multiple text loading
+ // this currently only supports 1 text at a time
+ // the struct amr_font_state_debug only has field for 1 font
+ // that can be fixed by adding an array object of a fixed size
+ // that user can initalize and will then support adding fonts to
+ // @TODO: look into refactoring this function, only did a basic pass
+ amr_MakeFontBitmapNaive(&State, &ArialFontState, text, text_sz);
// ---------- prepare bitmap texture and buffer objects -----------
////////////////////////////////// PREPARE TEXT FOR RENDERING ////////////
+ /// @resume: continue abstracting font rendering stuff into functions from here
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
f32 bmp_vertices[] = {
@@ -317,9 +254,9 @@ int main()
BufferO bo = CreateRectangleTextured(bmp_vertices, bmp_vsz, bmp_indices, bmp_isz, ATTR_TEX);
Texture2D _tex = {0};
{
- _tex.width = bitmap_width_from_text_info;
- _tex.height = bitmap_height_from_text_info;
- _tex.data = Bitmap;
+ _tex.width = ArialFontState.FontBitmapWidth;
+ _tex.height = ArialFontState.FontBitmapHeight;
+ _tex.data = ArialFontState.FontBitmap;
glBindVertexArray(bo.VAO);
glGenTextures(1, &bo.TexO);
@@ -369,12 +306,12 @@ int main()
// ////////////////////// DEFINE TEXT POSITION AND SCALE //////////////////
// ====================== TEXT POSITIONING AND SCALING ======
- Vec2 text_dims = {((f32)bitmap_width_from_text_info)/2.0f, ((f32)bitmap_height_from_text_info)/2.0f};
+ Vec2 text_dims = {((f32)ArialFontState.FontBitmapWidth)/2.0f, ((f32)ArialFontState.FontBitmapHeight)/2.0f};
Vec2 text_scaled_dims = State.px_ratio * text_dims;
Vec2 text_scaled_mid = text_scaled_dims/2.0f;
Vec2 text_pos = {0.0f, 0.0f};
- Vec2 text_offset = {text_dims.x/2.0f, (f32)fd_arr->baseline};
+ Vec2 text_offset = {text_dims.x/2.0f, ArialFontState.Baseline};
text_pos = text_offset + text_pos;
Vec2 text_scaled_pos = State.px_ratio*(State.ScreenCoords.tl + text_pos) + text_scaled_mid;
@@ -459,8 +396,8 @@ int main()
LoadUniformMat4(sp, "Projection", projection);
glEnable(GL_DEPTH_TEST);
- // glEnable(GL_BLEND);
- // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);