allegro5 icon indicating copy to clipboard operation
allegro5 copied to clipboard

[Enhancement] Font styles

Open damianday opened this issue 3 years ago • 13 comments
trafficstars

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.

damianday avatar Jun 06 '22 11:06 damianday

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.

beoran avatar Jun 06 '22 12:06 beoran

Oh, and al_do_multiline_ustr could also come in handy.

beoran avatar Jun 06 '22 12:06 beoran

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; }
};

damianday avatar Jun 19 '22 15:06 damianday

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.

beoran avatar Jun 19 '22 21:06 beoran

@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

pmprog avatar Jun 22 '22 20:06 pmprog

@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.

damianday avatar Jun 22 '22 20:06 damianday

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 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.

— 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: @.***>

EdgarReynaldo avatar Jun 23 '22 12:06 EdgarReynaldo

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.

beoran avatar Jun 23 '22 12:06 beoran

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 avatar Jun 23 '22 12:06 EdgarReynaldo

@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.

beoran avatar Jun 23 '22 12:06 beoran

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: @.***>

EdgarReynaldo avatar Jun 23 '22 12:06 EdgarReynaldo

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 ..

beoran avatar Jun 23 '22 13:06 beoran

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: @.***>

EdgarReynaldo avatar Jun 23 '22 13:06 EdgarReynaldo