Introduction
I am an avid fan of animes(Japanese animations). As I do not understand the Japanese language, the animes which I watched, have English subtitles. These fan-subbed animes have the most beautiful fonts and text. Below is a screenshot of the “Tales of the Abyss”, an anime based on a fantasy game with the same name.
I was fascinated by the outline text. I searched on the web for an outline text library which allows me to do outline text. Sadly I found none. Those that I found, are too difficult for me to retrofit them to my general purpose and I do not fully understand their sparsely commented codes. I decided to roll up my sleeves to write my own outline text library.
Single Outline
Above is an example outline text. Below is the generic GDI+ code to display such a text, using GraphicsPath class. Generally, to draw outline text, you have to get the text path, then render the outline with the text path and render the text by filling the interior of the path.
using namespace Gdiplus; Graphics graphics(dc.GetSafeHdc()); graphics.SetSmoothingMode(SmoothingModeAntiAlias); graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); Font font(pDC->GetSafeHdc(), &lf); GraphicsPath path(FillModeWinding); FontFamily fontFamily(L"Arial"); StringFormat strformat; wchar_t buf[] = L"CodeGuru Is The Best!"; // Add the string to the path path.AddString(buf,wcslen(buf),&fontFamily,FontStyleRegular,36,PointF(10.0f,10.0f),&strformat); // Draw the outline first Pen pen(Color(255,0,255),4); graphics.DrawPath(&pen, &path); // Draw the text by filling the path SolidBrush brush(Color(0,128,128)); graphics.FillPath(&brush, &path);
Before you rush to make your own outline library, I have to tell you one pitfall of GDI+. GDI+ cannot handle Postscript OpenType fonts; GDI+ can only handle TrueType fonts. I have searched for a solution and found Sjaak Priester’s Make GDI+ Less Finicky About Fonts. His approach is to parse the font file for its glyph and draw its outline. Sadly, I cannot use his code as his library is using the GNU license as I want to make my code free for all to use. I racked my brains for a solution. Since GDI(not GDI+) can display Postscript OpenType fonts and GDI supports path extraction through BeginPath/EndPath/GetPath. I decided to use just that to get my path into GDI+. Below is the comparison of the GDI+ path and GDI path. Note: Both are rendered by GDI+, it is just that their path extraction is different; one is using GDI+ while the other is using GDI to get the text path.
The top one is using GDI+ path and the bottom one is using GDI path. Looks like GDI paths are inferior and inaccurate. But GDI paths can do rotated text trick, like below, which GDI+ cannot do because GDI GraphicsPath’s AddString takes in a FontFamily object, not a Font object. My OutlineText class provides the GdiDrawString method if you have the need to use PostScript OpenType fonts. The effect below is a Franklin Gothic Demi font, size 36, Italic text rotated 10 degrees anti-clockwise.
Here is how to do text outline using my library.
using namespace Gdiplus; Graphics graphics(dc.GetSafeHdc()); graphics.SetSmoothingMode(SmoothingModeAntiAlias); graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); OutlineText text; text.TextOutline(Color(0,128,128),Color(255,0,255),4); text.DrawString(&graphics,&fontFamily,FontStyleRegular, 36, L"CodeGuru Is The Best!", Gdiplus::Point(10,10), &strformat);
Here is how to do rotated text using GDI
LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfHeight = -MulDiv(36, pDC->GetDeviceCaps(LOGPIXELSY), 72); lf.lfWeight = FW_NORMAL; lf.lfItalic = TRUE; lf.lfOrientation = 100; // 10 degrees lf.lfEscapement = 100; // 10 degrees lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; wcscpy_s(lf.lfFaceName, L"Arial"); // create and select it CFont newFont; if (!newFont.CreateFontIndirect(&lf)) return; CFont* pOldFont = pDC->SelectObject(&newFont); pDC->TextOut(10,80,L"CodeGuru Is The Best!", wcslen(L"CodeGuru Is The Best!")); // Put back the old font pDC->SelectObject(pOldFont);
Here is how to do rotated text outline using my library.
using namespace Gdiplus; Graphics graphics(dc.GetSafeHdc()); graphics.SetSmoothingMode(SmoothingModeAntiAlias); graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); OutlineText text; text.TextOutline(Color(255,0,0),Color(255,255,255),4); LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfHeight = -MulDiv(36, pDC->GetDeviceCaps(LOGPIXELSY), 72); lf.lfWeight = FW_NORMAL; lf.lfItalic = TRUE; lf.lfOrientation = 100; // 10 degrees lf.lfEscapement = 100; // 10 degrees lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; wcscpy_s(lf.lfFaceName, L"Franklin Gothic Demi"); text.GdiDrawString(&graphics,&lf,L"CodeGuru Is The Best!",Gdiplus::Point(10,80));