Fonts
I will use simple sprite fonts. It easy to implement.
I want to use TTF fonts, but I need to learn how to draw them.
Sprite fonts is good when you need beautiful colorful characters.
I don't want to create font generator, easy to add methods and add
characters by hands.
Add structure for character
struct slGUIFontGlyph
{
char32_t m_symbol = U' ';
slVec4f m_UV;
slRect m_rect;
int32_t m_underhang = 0;
int32_t m_overhang = 0;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint32_t m_textureSlot = 0;
};
Class for font
class slGUIFont
{
uint32_t m_textureNum = 0;
slArray<slTexture*> m_textures;
slArray<slGUIFontGlyph*> m_glyphs;
slPoint m_maxSize;
slGUIFontGlyph* m_glyphMap[0x32000];
public:
slGUIFont();
~slGUIFont();
void AddTexture(slTexture*);
void AddGlyph(const slGUIFontGlyph&);
void AddGlyph(char32_t ch, const slVec2f& leftTop, const slPoint& charSz, uint32_t texture, const slPoint& texSz)
{
slGUIFontGlyph glyph;
glyph.m_symbol = ch;
glyph.m_width = charSz.x;
glyph.m_height = charSz.y;
glyph.m_textureSlot = texture;
glyph.m_UV.x = slMath::coordToUV(leftTop.x, (float)texSz.x);
glyph.m_UV.y = slMath::coordToUV(leftTop.y, (float)texSz.y);
glyph.m_UV.z = slMath::coordToUV(leftTop.x + (float)charSz.x, (float)texSz.x);
glyph.m_UV.w = slMath::coordToUV(leftTop.y + (float)charSz.y, (float)texSz.y);
AddGlyph(glyph);
}
const slPoint& GetMaxSize() { return m_maxSize; }
slGUIFontGlyph** GetGlyphMap() { return m_glyphMap; }
slTexture* GetTexture(uint32_t i) { return m_textures.m_data[i]; }
int32_t m_characterSpacing = 1;
int32_t m_lineSpacing = 3;
int32_t m_spaceSize = 4;
int32_t m_tabSize = 16;
};
All glyphs stored in m_glyphs. m_glyphMap will have addresses.
0x32000 is current maximum unicode code, everything above is like reserved,
not printable
Implementation
slGUIFont::slGUIFont()
{
slGUIFontGlyph g;
g.m_symbol = 0;
AddGlyph(g);
for (uint32_t i = 0; i < 0x32000; ++i)
{
m_glyphMap[i] = m_glyphs.m_data[0];
}
}
slGUIFont::~slGUIFont()
{
for (size_t i = 0; i < m_glyphs.m_size; ++i)
{
slDestroy(m_glyphs.m_data[i]);
}
}
void slGUIFont::AddTexture(slTexture* t)
{
SL_ASSERT_ST(t);
m_textures.push_back(t);
}
void slGUIFont::AddGlyph(const slGUIFontGlyph& g)
{
// create new
slGUIFontGlyph* newG = slCreate<slGUIFontGlyph>();
// copy data
*newG = g;
// save it
m_glyphs.push_back(newG);
// save address
m_glyphMap[g.m_symbol] = newG;
if ((int32_t)g.m_height > m_maxSize.y) m_maxSize.y = g.m_height;
if ((int32_t)g.m_width > m_maxSize.x) m_maxSize.x = g.m_width;
}
Text drawing
GS must draw text. Without buffer creation.
You can create text like mesh object, but it not good if you want to draw a lot of text.
I want to draw not just text, but only one glyph. Because for text editor
I need to draw character by character. But for things like text on button I need to
give whole text. So add in GS two methods:
virtual void DrawGUICharacter(char32_t, slGUIFont*, const slVec2f& position, const slColor&) = 0;
virtual void DrawGUIText(const char32_t* text, uint32_t textSz, const slVec2f& position,
slGUIDrawTextCallback*) = 0;
In DrawGUIText I need to use callback to set the font and color.
What if we want to write japanese and other language in one text line?
void slGSD3D11::DrawGUICharacter(char32_t ch, slGUIFont* font, const slVec2f& position, const slColor& color)
{
SL_ASSERT_ST(font);
SL_ASSERT_ST(ch < 0x32000);
slGUIFontGlyph* g = font->GetGlyphMap()[ch];
slRect rct;
rct.left = (int32_t)position.x;
rct.top = (int32_t)position.y;
rct.right = rct.left + g->m_width;
rct.bottom = rct.top + g->m_height;
drawGUIRectangle(rct, color, color, (slGSD3D11Texture*)font->GetTexture(g->m_textureSlot),
&g->m_UV);
}
Draw something in app. Add glyphs.
slImage* myFontImage = slFramework::SummonImage(slFramework::GetPathA("../data/fonts/font.png").c_str());
slTextureInfo ti;
slTexture* myFontTexture = app.m_gs->SummonTexture(myFontImage, ti);
slGUIFont* myFont = slFramework::SummonFont();
myFont->AddTexture(myFontTexture);
slGUIFontGlyph glyph;
glyph.m_symbol = U'A';
glyph.m_height = 13;
glyph.m_width = 11;
glyph.m_textureSlot = 0;
glyph.m_UV.x = slMath::coordToUV(0, 256);
glyph.m_UV.y = slMath::coordToUV(0, 256);
glyph.m_UV.z = slMath::coordToUV(10, 256);
glyph.m_UV.w = slMath::coordToUV(12, 256);
myFont->AddGlyph(glyph);
myFont->AddGlyph(U'B', slVec4f(10, 0, 18, 12), slPoint(256, 256), slPoint(9, 13), 0);
myFont->AddGlyph(U'C', slVec2f(19, 0), slPoint(9, 13), 0, slPoint(256, 256));
Draw
app.m_gs->DrawGUICharacter(U'A', myFont, slVec2f(10.f, 10.f), ColorWhite);
app.m_gs->DrawGUICharacter(U'B', myFont, slVec2f(20.f, 10.f), ColorLime);
app.m_gs->DrawGUICharacter(U'C', myFont, slVec2f(30.f, 10.f), ColorCornsilk);
Now DrawGUIText
void slGSD3D11::DrawGUIText(const char32_t* text, uint32_t textSz, const slVec2f& _position,
slGUIDrawTextCallback* cb)
{
SL_ASSERT_ST(cb);
SL_ASSERT_ST(text);
slVec2f position = _position;
for (uint32_t i = 0; i < textSz; ++i)
{
slGUIFont* font = cb->OnFont(text[i]);
slColor* color = cb->OnColor(text[i]);
slGUIFontGlyph* g = font->GetGlyphMap()[text[i]];
slRect rct;
rct.left = (int32_t)position.x;
rct.top = (int32_t)position.y;
rct.right = rct.left + g->m_width;
rct.bottom = rct.top + g->m_height;
drawGUIRectangle(rct, *color, *color, (slGSD3D11Texture*)font->GetTexture(g->m_textureSlot),
&g->m_UV);
position.x += g->m_width + g->m_overhang + g->m_underhang + font->m_characterSpacing;
switch (text[i])
{
case U' ':
position.x += font->m_spaceSize;
break;
case U'\t':
position.x += font->m_tabSize;
break;
case U'\n':
position.y += font->m_lineSpacing + font->GetMaxSize().y;
position.x = _position.x;
break;
}
}
}
Callback
class slGUIDrawTextCallback : public slUserData
{
public:
slGUIDrawTextCallback() {}
virtual ~slGUIDrawTextCallback() {}
virtual slGUIFont* OnFont(char32_t) = 0;
virtual slColor* OnColor(char32_t) = 0;
};
Draw
class GUIDrawTextCallback : public slGUIDrawTextCallback
{
slGUIFont* m_font = 0;
slColor m_colorWhite = ColorWhite;
slColor m_colorRed = ColorRed;
public:
GUIDrawTextCallback(slGUIFont* f):m_font(f) {}
virtual ~GUIDrawTextCallback() {}
virtual slGUIFont* OnFont(char32_t c) final
{
return m_font;
}
virtual slColor* OnColor(char32_t c) final
{
switch (c)
{
case U'A':
case U'E':
case U'O':
case U'U':
case U'Y':
case U'I':
return &m_colorRed;
}
return &m_colorWhite;
}
};
...
app.m_gs->DrawGUIText(U"Hello world!\nNew LINE\nABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_)(abcdefghijklmnop<>", 79, slVec2f(0.f, 20.f), &textDrawCallback);
Other things
float slMath::coordToUV(float value, float textureSz)
{
return value * (1.f / textureSz);
}
Download
Please enable JavaScript to view the comments powered by Disqus.