Outline Text, Part 2

Introduction

Welcome to the Part 2 of Outline Text article series! You can read Outline Text Part 1 if you haven't had the chance to do so. In today's article, I will teach you how to implement translucent shadow in theory. Every C++ code sample is accompanied by an equivalent C# code sample; I have wrapped the native implementation, using C++/CLI for use in .Net WinForms applications; WPF has its own text outline implementation. I will also introduce a PngOutlineText class which renders text to a PixelFormat32bppARGB format Bitmap object which is a Bitmap object with an alpha channel, so that you need not regenerate the text, everytime you render it; you just blit that PNG bitmap.

Shadows?

Text shadow is drawn using the single outline text code. There is one problem: shadow is translucent. If we use the first code example to render shadows, it will turn out to be like the image below. It is because some area of the font body and font outline overlaps, so they are rendered twice, therefore it is darker.

My solution is to render the shadow text body and shadow text outline separately like below and combine them, with the pixels with shadow text body takes precedence; Only where the shadow text body is not rendered, the shadow text outline is rendered. Shadow rendering is more involved and complicated, the 0.1.0 version of OutlineText.cpp without shadow implementation is only 3KB in file size and has 164 lines of code while the 0.2.0 version of OutlineText.cpp with shadow implementation is 23KB in file size and has 865 lines of code! Therefore, I will not show its code here, you can download and read source code if you are interested.



Single Outline

This is the C++ code to use the OutlineText class to display the single outline text with a shadow. First, we need to enable the shadow, set its background image and set its attributes, like shadow color, width of the shadow outline and its offset point from the actual text.

#include "TextDesigner/OutlineText.h"

void CScratchPadDlg::OnPaint()
{
    //CDialog::OnPaint();
    CPaintDC dc(this);
    using namespace Gdiplus;
    Graphics graphics(dc.GetSafeHdc());
    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

    FontFamily fontFamily(L"Arial Black");
    StringFormat strformat;
    wchar_t pszbuf[] = L"Text Designer";

    OutlineText text;
    text.TextOutline(Color(255,128,64),Color(200,0,0),8);
    text.EnableShadow(true);
    CRect rect;
    GetClientRect(&rect);
    text.SetShadowBkgd(Color(255,255,0),rect.Width(),rect.Height());
    text.Shadow(Color(128,0,0,0), 4, Point(4,8));
    text.DrawString(&graphics,&fontFamily,FontStyleItalic, 48, pszbuf, Gdiplus::Point(10,10), &strformat);
}

This is the equivalent C# code to use the dotOutlineText class to display the single outline text with shadow.

private void OnPaint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

    FontFamily fontFamily = new FontFamily("Arial Black");
    StringFormat strformat = new StringFormat();
    string szbuf = "Text Designer";

    dotNetOutlineText text = new dotNetOutlineText();
    text.TextOutline(Color.FromArgb(255,128,64),Color.FromArgb(200,0,0),8);
    text.EnableShadow(true);
    text.SetShadowBkgd(Color.FromArgb(255,255,0),this.Size.Width, this.Size.Height);
    text.Shadow(Color.FromArgb(128,0,0,0), 4, new Point(4,8));
    text.DrawString(e.Graphics, fontFamily, FontStyle.Italic, 48, szbuf, new Point(10, 10), strformat);
}

This is the settings in TestOutlineText application to display the above. I list out the settings here because sometimes even I was a bit lost on how to use TestOutlineText to display certain outline text effects. By listing the settings here, I hope the readers will get familiar with this application, so that they can try out the outline effects they want.

Double Outline

To achieve double outline text, you have to specify the outer outline and the inner outline . This is the C++ code to display the double outline text.

#include "TextDesigner/OutlineText.h"

void CScratchPadDlg::OnPaint()
{
    //CDialog::OnPaint();
    CPaintDC dc(this);
    using namespace Gdiplus;
    Graphics graphics(dc.GetSafeHdc());
    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

    FontFamily fontFamily(L"Arial Black");
    StringFormat strformat;
    wchar_t pszbuf[] = L"Text Designer";

    OutlineText text;
    text.TextDblOutline(Color(255,255,255),Color(0,128,128),Color(0,255,0),4,4);
    text.EnableShadow(true);
    CRect rect;
    GetClientRect(&rect);
    text.SetShadowBkgd(Color(255,128,192),rect.Width(),rect.Height());
    text.Shadow(Color(128,0,0,0), 4, Point(4,8));
    text.DrawString(&graphics,&fontFamily,FontStyleRegular, 48, pszbuf, Gdiplus::Point(10,10), &strformat);
}

This is the C# code to display the double outline text.

private void OnPaint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

    FontFamily fontFamily = new FontFamily("Arial Black");
    StringFormat strformat = new StringFormat();
    string szbuf = "Text Designer";

    dotNetOutlineText text = new dotNetOutlineText();
    text.TextDblOutline(Color.FromArgb(255,255,255),Color.FromArgb(0,128,128),Color.FromArgb(0,255,0),4,4);
    text.EnableShadow(true);
    text.SetShadowBkgd(Color.FromArgb(255,128,192),this.Size.Width, this.Size.Height);
    text.Shadow(Color.FromArgb(128,0,0,0), 4, new Point(4,8));
    text.DrawString(e.Graphics, fontFamily, FontStyle.Bold, 48, szbuf, new Point(10, 10), strformat);
}

This is the settings to display the double outline text.

Outline Text, Part 2

Text Glow

[TextGlow.PNG]

This is the C++ code to display the text glow. Text glow is usually not displayed with a shadow because shadow interfere with the glow effect so I disabled the shadow and did not set any shadow settings.

#include "TextDesigner/OutlineText.h"

void CScratchPadDlg::OnPaint()
{
    //CDialog::OnPaint();
    CPaintDC dc(this);
    using namespace Gdiplus;
    Graphics graphics(dc.GetSafeHdc());
    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

    FontFamily fontFamily(L"Arial Black");
    StringFormat strformat;
    wchar_t pszbuf[] = L"Text Designer";

    OutlineText text;
    text.TextGlow(Color(191,255,255),Color(24,0,128,128),14);
    text.EnableShadow(false);
    text.DrawString(&graphics,&fontFamily,FontStyleRegular, 48, pszbuf, Gdiplus::Point(10,10), &strformat);
}

This is the similar C# code to display the text glow.

private void OnPaint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

    FontFamily fontFamily = new FontFamily("Arial Black");
    StringFormat strformat = new StringFormat();
    string szbuf = "Text Designer";

    dotNetOutlineText text = new dotNetOutlineText();
    text.TextGlow(Color.FromArgb(191,255,255),Color.FromArgb(24,0,128,128),14);
    text.EnableShadow(false);
    text.DrawString(e.Graphics, fontFamily, FontStyle.Bold, 48, szbuf, new Point(10, 10), strformat);
}

This is the settings to display the text glow.

[TextGlowSettings.PNG]

This is text glow with shadow if you are curious.

[TextGlowWithShadow.PNG]

Fake 3D Text

You can achieve simulated 3D text by using a bigger and opaque shadow which has the same colour as the outline colour. Of course, if you look closely enough, you know it is not looking like 3D at all.

[Fake3DTextSettings.PNG]

Rotated Text

[RotatedText.PNG]

We have to use GdiDrawString method to display the rotated text because GdiDrawString takes in a LOGFONT structure which allows us to specify rotational angle through the lfEscapement and lfOrientation. While DrawString method uses AddString method, of GraphicsPath, which takes in a font family object, instead of a font object, we do not have the ability to specify the rotational angle. Note: the rotational degrees specified should be multiplied by 10, that is 100 is actually 10 degrees.

#include "TextDesigner/OutlineText.h"

void CScratchPadDlg::OnPaint()
{
    //CDialog::OnPaint();
    CPaintDC dc(this);
    using namespace Gdiplus;
    
	Graphics graphics(dc.GetSafeHdc());
    graphics.SetSmoothingMode(SmoothingModeAntiAlias);
    graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);

    wchar_t pszbuf[] = L"Text Designer";

    LOGFONTW logfont;
    memset(&logfont, 0, sizeof(logfont));
    wcscpy_s(logfont.lfFaceName, L"Arial Black");
    logfont.lfHeight = -MulDiv(48, dc.GetDeviceCaps(LOGPIXELSY), 72);
    logfont.lfEscapement = 100;
    logfont.lfOrientation = 100;
    logfont.lfItalic = 1;

    OutlineText text;
    text.TextOutline(Color(64,193,255),Color(0,0,0),8);
    text.EnableShadow(false);
    CRect rect;
    GetClientRect(&rect);
    text.EnableShadow(true);
    text.SetShadowBkgd(Color(255,255,255),rect.Width(),rect.Height());
    text.Shadow(Color(128,0,0,0), 8, Point(4,4));
    text.GdiDrawString(&graphics, &logfont, pszbuf, Gdiplus::Point(10,100));
}

This is the C# code to display the rotated text. The GdiDrawString of the dotNetOutlineText has many parameters which are to fill the LOGFONT structure. I cannot make GdiDrawString method to take in LOGFONT structure directly because this structure only exists in the Compact Edition of .Net framework.

private void OnPaint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

    SolidBrush brushWhite = new SolidBrush(Color.White);
    e.Graphics.FillRectangle(brushWhite, 0, 0, this.ClientSize.Width, this.ClientSize.Height);

    string szbuf = "Text Designer";

    dotNetOutlineText text = new dotNetOutlineText();

    text.TextOutline(Color.FromArgb(64, 193, 255), Color.FromArgb(0, 0, 0), 8);
    text.SetShadowBkgd(Color.White, this.Size.Width, this.Size.Height);

    text.EnableShadow(true);
    text.Shadow(Color.FromArgb(128, 0, 0, 0), 4, new Point(4, 4));
    text.GdiDrawString(
        e.Graphics, 			
        "Arial Black", // lfFaceName
        100, // lfHeight
        700, // lfWeight // 700 is bold
        1, // lfItalic
        0, // lfOutPrecision
        100, // lfEscapement
        100, // lfOrientation
        szbuf,  // szText
        new Point(10, 100) // ptDraw
		);
}

This is the settings to display the rotated text.

[RotatedTextSettings.PNG]

PngOutlineText

I have written a PngOutlineText class which renders the text and shadow to a Bitmap object with an alpha channel(PixelFormat32bppARGB format), so that you need not re-generate the text whenever you need to render the text again because outline text generation typically takes a long time, especially for text with shadow. Using PngOutlineText, you need to call the SetPngImage method to set the PixelFormat32bppARGB format image. After the first DrawString or GdiDrawString, you need just to blit this image to your graphics object, instead of generating same outline text through DrawString or GdiDrawString again. I create PngOutlineText class for use in video rendering which is typically about 30fps or 60fps. If you use a big image background in TestOutlineText application, you will find resizing the application and scrolling the image is not smooth. If you check "Enable Png Rendering" checkbox(See the highlighted red rectangle below), resizing and scrolling becomes smooth because the TestOutlineText application detects if the settings has not been modified, it will just blit the transparent text image instead. You can use the SavePngImage method to save the image to PNG image. If you open the image in any image editor, like Paint.Net or Photoshop, you will see the checkered boxes which is the transparent part of the PNG image.

[EnablePngSettings3.JPG]
[PngInEditor.PNG]

OpenGL Demo

I have made a OpenGL demo which shows how to use images with alpha channel as textures. I used to work in muvee, a company, which specializes in making automatic video editing software, uses OpenGL to do their video special effects with the photos. I find their text captions, used in their automatic edited video, quite bland. I hope they will adopt my Text Designer Outline Text library in their next version of muvee software

[OpenGLScreenshot1.jpg]

[OpenGLScreenshot2.jpg]

Codeplex

Text Designer Outline Text open source library is hosted in Codeplex at this url: http://outlinetext.codeplex.com/ . The library at Codeplex will be updated faster than the one on this article, so be sure to check the library codeplex for updated source, from time to time. The version of Text Designer is currently at 0.2.0 . There is a lot more work(80% more) to be done before this library achieve version 1.0.0 . The eventual goal of this library is to get as advanced as WPF text effects. In the final installation of the article, I'll teach you how to make your own special effects with the text. The possibilities of effects is only virtually limitless and is only limited by your own imagination! Thanks for reading!



About the Author

Wong Shao Voon

I guess I'll write here what I does in my free time, than to write an accolade of skills which I currently possess. I believe the things I does in my free time, say more about me.

When I am not working, I like to watch Japanese anime. I am also writing some movie script, hoping to see my own movie on the big screen one day.

I like to jog because it makes me feel good, having done something meaningful in the morning before the day starts.

I also writes articles for CodeGuru; I have a few ideas to write about but never get around writing because of hectic schedule.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • You may already know about some of the benefits of Bluemix, IBM's open platform for developing and deploying mobile and web applications. Check out this webcast that focuses on building an Android application using the MobileData service, with a walk-through of the real process and workflow used to build and link the MobileData service within your application. Join IBM's subject matter experts as they show you the way to build a base application that will jumpstart you into building your own more complex app …

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds