allegro5
allegro5 copied to clipboard
[Enhancement] Font styles
I love using Allegro5 but I find myself always having to manually code these for each project, which is a PITA. It would be great if they would come as standard. On a side note other libraries have some support, SDL2 has a font addon and SFML2 can support some of these.
Features I would like to see included are:
- Italic
- Underline
- Strikethrough
- Weight
- Specify a background color (similar to TextOut and SetBkColor from Win32 API).
Thanks for reading.
Basically, this is what the per glyph text drawing API is for. It can be used to implement arbitrary text effects per glyph. https://liballeg.org/a5docs/5.2.4/font.html#per-glyph-text-handling. Example: https://github.com/liballeg/allegro5/blob/edd102d8f6fecc7413a9a3343978c3d22855fca4/examples/ex_ttf.c
Of course if you would like to contribute someone like an AL_STYLED_UTF8, with per glyph or per glyph span styling, that would be welcome. Underline and strikethrough simply require line drawing. Shadow can be done by drawing twice. Bold and italic by applying a matrix. And a background is a background colored rectangle or bitmap.
Oh, and al_do_multiline_ustr could also come in handy.
I've added my own Font class which I've came up, if I get time I could try to integrate it into the allegro5 repo.
#define TA_TOP 0x00000000
#define TA_LEFT 0x00000000
#define TA_CENTER 0x00000001
#define TA_RIGHT 0x00000002
#define TA_VCENTER 0x00000004
#define TA_BOTTOM 0x00000008
class Font
{
private:
INT mWeight;
bool mItalic;
bool mULine;
bool mStrikeOut;
ALLEGRO_FONT* mFont;
public:
Font()
{
mFont = nullptr;
mWeight = FW_NORMAL;
mItalic = false;
mULine = false;
mStrikeOut = false;
}
~Font()
{
UnloadFont();
}
bool LoadFromFile(const char* fileName, INT size, INT nWeight = FW_NORMAL, bool bItalic = false, bool bULine = false, bool bStrikeOut = false)
{
char szFullPath[128];
sprintf(szFullPath, ".\\Fonts\\%s.ttf", fileName);
auto handle = al_load_font(szFullPath, size, ALLEGRO_TTF_MONOCHROME);
if (handle == nullptr)
return false;
mWeight = nWeight;
mItalic = bItalic;
mULine = bULine;
mStrikeOut = bStrikeOut;
mFont = handle;
//mOwnsFont = true;
return true;
}
VOID UnloadFont()
{
//if (!mOwnsFont) return;
if (mFont != nullptr)
{
al_destroy_font(mFont);
mFont = nullptr;
}
}
VOID DrawText(const char* str, Color foreColor, Color backColor, float fXPos, float fYPos, float fWidth, float fHeight, UINT format = TA_LEFT | TA_TOP)
{
auto alColor = al_map_rgba(foreColor.r, foreColor.g, foreColor.b, foreColor.a);
auto alColor2 = al_map_rgba(backColor.r, backColor.g, backColor.b, backColor.a);
auto metrics = MeasureText(str);
if (fWidth == 0 || fHeight == 0)
{
fWidth = metrics.cx;
fHeight = metrics.cy;
}
Vector2 pos;
if ((format & TA_LEFT) == TA_LEFT)
pos.x = fXPos;
if ((format & TA_CENTER) == TA_CENTER)
pos.x = fXPos + (fWidth - metrics.cx) * 0.5f;
if ((format & TA_RIGHT) == TA_RIGHT)
pos.x = fXPos + (fWidth - metrics.cx);
if ((format & TA_TOP) == TA_TOP)
pos.y = fYPos;
if ((format & TA_VCENTER) == TA_VCENTER)
pos.y = fYPos + (fHeight - metrics.cy) * 0.5f;
if ((format & TA_BOTTOM) == TA_BOTTOM)
pos.y = fYPos + (fHeight - metrics.cy);
if (backColor.a > 0)
al_draw_filled_rectangle(pos.x, pos.y, pos.x + fWidth, pos.y + fHeight, alColor2);
ALLEGRO_TRANSFORM prevTrans, curTrans;
if (mItalic)
{
al_copy_transform(&prevTrans, al_get_current_transform());
al_identity_transform(&curTrans);
al_horizontal_shear_transform(&curTrans, 0.3f);
al_compose_transform(&curTrans, &prevTrans);
al_use_transform(&curTrans);
}
al_draw_text(mFont, alColor, pos.x, pos.y, ALLEGRO_ALIGN_LEFT, str);
if (mULine)
al_draw_line(pos.x, pos.y + (fHeight + 0.5f), pos.x + fWidth, pos.y + (fHeight + 0.5f), alColor, 0.5f);
if (mStrikeOut)
al_draw_line(pos.x, pos.y + (fHeight * 0.5f), pos.x + fWidth, pos.y + (fHeight * 0.5f), alColor, 0.5f);
if (mItalic)
al_use_transform(&prevTrans);
}
VOID DrawText(const char* str, Color foreColor, Color backColor, float x, float y)
{
DrawText(str, foreColor, backColor, x, y, 0, 0);
}
INT GetLineHeight()
{
if (mFont == nullptr)
return 0;
return al_get_font_line_height(mFont);
}
SIZE MeasureText(const char* str)
{
INT bbx = 0, bby = 0, bbw = 0, bbh = 0;
SIZE metrics = { 0, 0 };
if (mFont == nullptr)
return metrics;
//metrics.cx = al_get_text_width(mFont, str);
//metrics.cy = al_get_font_line_height(mFont);
al_get_text_dimensions(mFont, str, &bbx, &bby, &bbw, &bbh);
metrics.cx = bbx + bbw;
metrics.cy = bby + bbh;
return metrics;
}
ALLEGRO_FONT* GetHandle() { return mFont; }
};
Allegro is a library with a C API, so we cannot accept a C++ interface. But you could set this up as a C++ library in a separate Git repository, or change this to C functions.
@damianday, Your MeasureText function would not provide a correct result if you set Italic, because it doesn't cater for the skew. Won't necessarily matter in all cases, but if you're mixing italics and non-italics on the same line measuring the text width for positioning, might give you overlapping.
Most of the TTF fonts I've dealt with have an italic version as a separate TTF file, so you could load both and get font measurements there
@damianday, Your MeasureText function would not provide a correct result if you set Italic, because it doesn't cater for the skew. Won't necessarily matter in all cases, but if you're mixing italics and non-italics on the same line measuring the text width for positioning, might give you overlapping.
Most of the TTF fonts I've dealt with have an italic version as a separate TTF file, so you could load both and get font measurements there
Yeah agree a few bugs, given the added complexity of italics the simplest option would be to load a separate font, same would be true for fonts with different weights which isn't implemented here.
I was going to implement something similar in my GUI library, but decided it was actually much easier to just load separate fonts for bold / italic / etc...
But if you can come up with a global solution it would still be cool.
On 6/22/2022 3:37 PM, Damian Day wrote:
@damianday <https://github.com/damianday>, Your MeasureText function would not provide a correct result if you set Italic, because it doesn't cater for the skew. Won't necessarily matter in all cases, but if you're mixing italics and non-italics on the same line measuring the text width for positioning, might give you overlapping. Most of the TTF fonts I've dealt with have an italic version as a separate TTF file, so you could load both and get font measurements thereYeah agree a few bugs, given the added complexity of italics the simplest option would be to load a separate font, same would be true for fonts with different weights which isn't implemented here.
— Reply to this email directly, view it on GitHub https://github.com/liballeg/allegro5/issues/1340#issuecomment-1163576713, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF2JBYKAUTTR73DC2B7LCPTVQN2PRANCNFSM5X7D2MJA. You are receiving this because you are subscribed to this thread.Message ID: @.***>
Yes, it should be possible to apply the graphics matrix also to the dimensions of the transformed italic or bold characters to get the correct size. It is convenient not to have to load several fonts for one text.
One caveat - individual fonts are tailored to work specifically for italic or bold fonts. Using transforms and drawing tricks is not always sufficient to get a proper appearance to the font. If you can show me some samples of your transformed fonts I would appreciate it.
On 6/23/2022 7:48 AM, Beoran wrote:
Yes, it should be possible to apply the graphics matrix also to the dimensions of the transformed italic or bold characters to get the correct size. It is convenient not to have to load several fonts for one text.
— Reply to this email directly, view it on GitHub https://github.com/liballeg/allegro5/issues/1340#issuecomment-1164366759, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF2JBYMLHHXBHV3UPJRA6L3VQRMJRANCNFSM5X7D2MJA. You are receiving this because you commented.Message ID: @.***>
@EdgarReynaldo that is certainly true for several fonts where transforms don't work well, but for certain fonts, like some bitmap fonts, it is good enough.
How do you plan to implement bold styles? You can't just underdraw a shadow of the text at a larger font - it messes with the spacing. Does Freetype2 have any methods for implementing font styles? We could just add this directly to the ttf addon or font addon as appropriate. I also had a dream where I implemented real time drawing of glyphs without caching, but who knows about that coming true.
Marc
On 6/23/2022 7:53 AM, Beoran wrote:
@EdgarReynaldo https://github.com/EdgarReynaldo that is certainly true for several fonts where transforms don't work well, but for certain fonts, like some bitmap fonts, it is good enough.
— Reply to this email directly, view it on GitHub https://github.com/liballeg/allegro5/issues/1340#issuecomment-1164371813, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF2JBYLK5ULY424CYT4YABLVQRM35ANCNFSM5X7D2MJA. You are receiving this because you were mentioned.Message ID: @.***>
FT_Set_Char_Size, FT_Set_Pixel_Sizes, FT_Get_Transform and FT_Set_Transform seem like useful here, at least for simulating font styles, but there is no specific API. Drawing the splines of the TTF glyphs ourselves using the primitive addon would of course give us even more flexibility, but it would be a lot of work to actually implement this ..
Yeah, it would be hard to implement non-cached drawing of glyphs. Maybe someday I'll get around to it. But not anytime soon. Got a lot on my own plate.
Thanks for looking into this Beoran.
Marc
On 6/23/2022 8:25 AM, Beoran wrote:
FT_Set_Char_Size, FT_Set_Pixel_Sizes, FT_Get_Transform and FT_Set_Transform seem like useful here, at least for simulating font styles, but there is no specific API. Drawing the splines of the TTF glyphs ourselves using the primitive addon would of course give us even more flexibility, but it would be a lot of work to actually implement this ..
— Reply to this email directly, view it on GitHub https://github.com/liballeg/allegro5/issues/1340#issuecomment-1164406181, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF2JBYID2QS5VS3MBQG347LVQRQVBANCNFSM5X7D2MJA. You are receiving this because you were mentioned.Message ID: @.***>