GDI+ FAQ: Drawing text with a drop shadow effect
GDI+'s graphics abilities are
excellent for text and graphical "special effects". One of the nicer effects is to put
a drop shadow on text.
Many simple text effects simply
involve drawing the text two or more times using different colours and
positions. Drop shadows however take advantage of GDI+'s antialiasing
capabilities to produce alpha blended shadows with delicious umbra and
penumbra shadows.
The trick to this is to first draw
the text smaller than you want it to appear on the drawing surface and
then blow it up by stretching it.
The steps to take are as
follows:
-
Create a Bitmap in memory who's dimensions are some
sub-multiple of the current window (for this example I use a ¼
size)
-
Set up a matrix to make the text ¼ of the size and move it
¼ of the distance that you want the drop shadow to be from the
"original"
-
Draw the text to the bitmap using an antialiased drawing
mode and a pen that has an alpha component (say 50% transparency).
Remember that when newly created in memory, bitmaps are already 100%
transparent so the bits we add make non-transparent areas in the image
which show up as artifacts on the screen after blitting..
-
Blit the bitmap to the main screen, expanding it by 4:1 in
both directions using a HighQualityBicubic interpolation mode. This is
very important because the bicubic sampling makes the edges of the text
fuzzy and indistinct and provide the umbra and penumbra effect.
-
Finally, draw the original text onto the main drawing
surface. Once again an antialiased drawing mode should be used to ensure
correct registration of the text with its shadow. For this purpose the
TextRenderingHint.Antialias is best.
After completing these steps, an
effect similar to that seen in Figure 1 is possible.

Figure 1.
The application that produced this
effect is listed in its entirety below. The Form1_Paint event handler is
the bit that does the work.
using
System;
using
System.Drawing;
using
System.Drawing.Text;
using
System.Drawing.Drawing2D;
using
System.Collections;
using
System.ComponentModel;
using
System.Windows.Forms;
using
System.Data;
namespace
textdropshadow
{
/// <summary>
/// Summary
description for Form1.
/// </summary>
public class
Form1 : System.Windows.Forms.Form
{
///
<summary>
///
Required designer variable.
///
</summary>
private
System.ComponentModel.Container components = null;
public
Form1()
{
//
// Required for Windows Form Designer
support
//
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw,true);
}
///
<summary>
///
Clean up any resources being used.
///
</summary>
protected override void
Dispose( bool disposing
)
{
if( disposing
)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing
);
}
#region
Windows Form Designer generated code
///
<summary>
///
Required method for Designer support - do not
modify
/// the
contents of this method with the code editor.
///
</summary>
private void
InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(16,
36);
this.BackColor =
System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(376,
293);
this.Font = new System.Drawing.Font("Tahoma", 21.75F,
System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.Name =
"Form1";
this.Text =
"Form1";
this.Paint += new
System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
}
#endregion
///
<summary>
/// The
main entry point for the application.
///
</summary>
[STAThread]
static void Main()
{
Application.Run(new
Form1());
}
protected override void
OnPaintBackground(PaintEventArgs e)
{
LinearGradientBrush b=new
LinearGradientBrush(this.ClientRectangle,Color.Blue,Color.AliceBlue,90f);
e.Graphics.FillRectangle(b,this.ClientRectangle);
b.Dispose();
}
private void Form1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
//Make a small bitmap
Bitmap bm=new Bitmap(this.ClientSize.Width/4,this.ClientSize.Height/4);
//Get a graphics object for it
Graphics g=Graphics.FromImage(bm);
// must use an antialiased rendering hint
g.TextRenderingHint=TextRenderingHint.AntiAlias;
//this matrix zooms the text out to 1/4 size and offsets it by a
little right and down
Matrix mx=new
Matrix(0.25f,0,0,0.25f,3,3);
g.Transform=mx;
//The shadow is drawn
g.DrawString("Text with a dropshadow",Font,new SolidBrush( Color.FromArgb(128,
Color.Black)), 10, 10, StringFormat.GenericTypographic
);
//Don’t need this anymore
g.Dispose();
//The destination Graphics uses a high quality
mode
e.Graphics.InterpolationMode=InterpolationMode.HighQualityBicubic;
//and draws antialiased text for accurate
fitting
e.Graphics.TextRenderingHint=TextRenderingHint.AntiAlias;
//The small image is blown up to fill the main client
rectangle
e.Graphics.DrawImage(bm,this.ClientRectangle,0,0,bm.Width,bm.Height,GraphicsUnit.Pixel);
//finally, the text is drawn on top
e.Graphics.DrawString("Text with a
dropshadow",Font,Brushes.White,10,10,StringFormat.GenericTypographic);
bm.Dispose();
}
}
}
Back to the GDI+
FAQ
|