summaryrefslogtreecommitdiff
path: root/code/amr_fonts.h
blob: 81666a0bfb51c1eed1daeebf1f2cee39f7680108 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#ifndef AMR_FONTS_H
#define AMR_FONTS_H

#define LATIN_BASIC_START_INDEX 32
#define LATIN_BASIC_END_INDEX 126

typedef struct amr_letter_details_debug {
  i32 advance;
  i32 lsb;
  i32 x0, y0, x1, y1;
  i32 byte_offset;
  // @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
// - font buffer, must be initialised by caller
// - size of the font file, must be known beforehand
// output:
// - error code
u32 amr_InitFont(const u8 *FontPath, u8 *FontBuffer, u32 FontFz); 
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)
{
  PlatformDebugFileRead(FontPath, FontBuffer, FontSz);
  if (!stbtt_InitFont(font_info, FontBuffer, 0))
  {
    printf("ERROR: failed to read arial font");
    assert(1 == 0);
  }

  return 0;
}

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 = *(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