2023-06-17 17:00:55 +00:00
|
|
|
#define GLYPH_SUBPIXEL_SEGMENTS 3
|
|
|
|
|
|
|
|
inline s32 GetSubpixelSegmentAtP(r32 Value)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS;
|
|
|
|
return(Result);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RasterizeGlyph(glyph_atlas *Atlas, font_id Font, glyph *Glyph, u32 Codepoint, r32 Size, s32 Subpixel)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
Glyph->Font = Font;
|
|
|
|
Glyph->Codepoint = Codepoint;
|
|
|
|
Glyph->Size = Size;
|
|
|
|
Glyph->Subpixel = Subpixel;
|
|
|
|
|
|
|
|
Assert(Subpixel < GLYPH_SUBPIXEL_SEGMENTS);
|
|
|
|
|
|
|
|
loaded_font *LoadedFont = Atlas->Fonts + Font;
|
|
|
|
stbtt_fontinfo *Info = &LoadedFont->Info;
|
|
|
|
|
|
|
|
r32 Scale = stbtt_ScaleForMappingEmToPixels(Info, Size);
|
|
|
|
|
|
|
|
s32 InternalIndex = (s32)(Glyph - Atlas->Glyphs);
|
|
|
|
s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize;
|
|
|
|
|
|
|
|
v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize,
|
|
|
|
(InternalIndex / GlyphsPerRow)*Atlas->GlyphSize);
|
|
|
|
|
|
|
|
int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint);
|
|
|
|
|
|
|
|
stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale,
|
|
|
|
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
|
|
|
&Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y);
|
|
|
|
|
|
|
|
Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize);
|
|
|
|
stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer,
|
|
|
|
Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize,
|
|
|
|
Scale, Scale,
|
2023-06-17 17:00:55 +00:00
|
|
|
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
2023-06-21 16:59:36 +00:00
|
|
|
GlyphIndex);
|
|
|
|
|
|
|
|
s32 Advance, LeftSideBearing;
|
|
|
|
stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing);
|
|
|
|
Glyph->Advance = Advance*Scale;
|
|
|
|
Glyph->Offset.x = LeftSideBearing*Scale;
|
|
|
|
|
|
|
|
Glyph->Offset.y = Glyph->P0.y + (LoadedFont->Ascent + LoadedFont->LineGap)*Scale;
|
|
|
|
|
|
|
|
v2_s32 Dim = Glyph->P1 - Glyph->P0;
|
|
|
|
|
|
|
|
Glyph->P0 = BaseTextureOffset;
|
|
|
|
Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2);
|
|
|
|
|
|
|
|
Atlas->RenderCommands->FillRegion(Atlas->Texture,
|
|
|
|
BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize),
|
|
|
|
Atlas->BitmapBuffer);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static glyph *GetGlyph(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size, s32 Subpixel)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
glyph *Glyph = 0;
|
|
|
|
|
|
|
|
for(s32 GlyphIndex = 0;
|
|
|
|
GlyphIndex < Atlas->GlyphsUsed;
|
|
|
|
++GlyphIndex)
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
glyph *At = Atlas->Glyphs + GlyphIndex;
|
|
|
|
if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel))
|
|
|
|
{
|
|
|
|
Glyph = At;
|
|
|
|
break;
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
2023-06-21 16:59:36 +00:00
|
|
|
|
|
|
|
if(Glyph)
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
if(Atlas->GlyphsUsed < Atlas->MaxGlyphCount)
|
|
|
|
{
|
|
|
|
Glyph = Atlas->Glyphs + Atlas->GlyphsUsed++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Glyph = Atlas->LRUFirst;
|
|
|
|
Assert(Glyph);
|
|
|
|
|
|
|
|
DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
|
|
|
|
}
|
|
|
|
|
|
|
|
RasterizeGlyph(Atlas, Font, Glyph, Codepoint, Size, Subpixel);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-21 16:59:36 +00:00
|
|
|
DLLInsertLast_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev);
|
|
|
|
|
|
|
|
return(Glyph);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands,
|
|
|
|
s32 BitmapSize = DEFAULT_GLYPH_ATLAS_DIM,
|
|
|
|
s32 GlyphSize = MAX_GLYPH_SIZE)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
glyph_atlas *Atlas = BootstrapPushStruct(glyph_atlas, Arena);
|
|
|
|
|
|
|
|
Atlas->BitmapSize = BitmapSize;
|
|
|
|
Atlas->GlyphSize = GlyphSize;
|
|
|
|
|
|
|
|
Atlas->MaxGlyphCount = (DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE)*(DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE);
|
|
|
|
Atlas->Glyphs = PushArray(&Atlas->Arena, glyph, Atlas->MaxGlyphCount);
|
|
|
|
|
|
|
|
Atlas->RenderCommands = RenderCommands;
|
|
|
|
Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, 0);
|
|
|
|
|
|
|
|
Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf"));
|
|
|
|
Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf"));
|
|
|
|
Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/Liberation-Mono.ttf"));
|
2023-06-27 14:14:28 +00:00
|
|
|
Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf"));
|
2023-06-21 16:59:36 +00:00
|
|
|
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(&Atlas->Arena, StrLit("fonts/icons.ttf"));
|
|
|
|
|
|
|
|
for(s32 FontIndex = 0;
|
|
|
|
FontIndex < Font_Count;
|
|
|
|
++FontIndex)
|
|
|
|
{
|
|
|
|
loaded_font *Font = Atlas->Fonts + FontIndex;
|
|
|
|
stbtt_InitFont(&Font->Info,
|
|
|
|
Font->Data.Data,
|
|
|
|
stbtt_GetFontOffsetForIndex(Font->Data.Data, 0));
|
|
|
|
|
|
|
|
stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap);
|
|
|
|
}
|
|
|
|
|
|
|
|
Atlas->BitmapBuffer = PushArray(&Atlas->Arena, u8, GlyphSize*GlyphSize);
|
|
|
|
|
|
|
|
return(Atlas);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
|
|
|
v2 P, r32 Size, v4 Color,
|
|
|
|
string Text)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
r32 Oversample = 2;
|
|
|
|
|
|
|
|
for(utf8_iterator Iter = IterateUTF8String(Text);
|
|
|
|
IsValid(&Iter);
|
|
|
|
Advance(&Iter))
|
|
|
|
{
|
|
|
|
u32 Codepoint = Iter.Codepoint;
|
|
|
|
|
|
|
|
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(P.x*Oversample));
|
|
|
|
Assert(Glyph);
|
|
|
|
|
|
|
|
v2 GlyphP = P;
|
|
|
|
GlyphP.x += Glyph->Offset.x/Oversample;
|
|
|
|
GlyphP.y += Glyph->Offset.y/Oversample;
|
|
|
|
|
|
|
|
v2 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0);
|
|
|
|
v2 Dim = RenderDim;
|
|
|
|
Dim.x /= Oversample;
|
|
|
|
Dim.y /= Oversample;
|
|
|
|
|
|
|
|
PushTexturedQuad(Group, GlyphP, Dim, ConvertV2ToR32(Glyph->P0), RenderDim,
|
|
|
|
Color, Color, Color, Color, 0, 0, 0, Atlas->Texture);
|
|
|
|
|
|
|
|
P.x += Glyph->Advance/Oversample;
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
|
|
|
v2 P, r32 Size, v4 Color,
|
|
|
|
char *Format, ...)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
temporary_memory Scratch = GetScratch(0, 0);
|
|
|
|
|
|
|
|
va_list Arguments;
|
|
|
|
va_start(Arguments, Format);
|
|
|
|
string String = PushFormatVariadic(Scratch.Arena, Format, Arguments);
|
|
|
|
va_end(Arguments);
|
|
|
|
|
|
|
|
PushText(Group, Atlas, Font, P, Size, Color, String);
|
|
|
|
|
|
|
|
ReleaseScratch(Scratch);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Size, string Text)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
r32 Oversample = 2;
|
|
|
|
|
|
|
|
r32 X = 0;
|
|
|
|
|
|
|
|
for(utf8_iterator Iter = IterateUTF8String(Text);
|
|
|
|
Iter.Codepoint != 0;
|
|
|
|
Advance(&Iter))
|
|
|
|
{
|
|
|
|
u32 Codepoint = Iter.Codepoint;
|
|
|
|
|
|
|
|
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Oversample, GetSubpixelSegmentAtP(X*Oversample));
|
|
|
|
Assert(Glyph);
|
|
|
|
|
|
|
|
X += Glyph->Advance/Oversample;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(X);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline r32 CalculateRasterizedTextHeight(glyph_atlas *Atlas, font_id Font, r32 Size, string Text)
|
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
r32 Scale = stbtt_ScaleForMappingEmToPixels(&Atlas->Fonts[Font].Info, Size)/
|
|
|
|
stbtt_ScaleForPixelHeight(&Atlas->Fonts[Font].Info, Size);
|
|
|
|
|
|
|
|
r32 Y = Size*Scale;
|
|
|
|
|
|
|
|
for(utf8_iterator Iter = IterateUTF8String(Text);
|
|
|
|
Iter.Codepoint != 0;
|
|
|
|
Advance(&Iter))
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-06-21 16:59:36 +00:00
|
|
|
u32 Codepoint = Iter.Codepoint;
|
|
|
|
|
|
|
|
if(Codepoint == '\n')
|
|
|
|
{
|
|
|
|
Y += Size*Scale;
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
2023-06-21 16:59:36 +00:00
|
|
|
return(Y);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|