Introduction
In this article, I'll show you how to convert a TTF character to a bitmap file with the option to define the color depth of the bitmap picture. You can use this code to create icons for your application by extracting the appropriate characters from a Windows font file. For example, you can see that the Wingdings MS Word font is full of characters that can be used like cool icons.
Step 1
The first step is to create a BMP file. In the attached code, you can see the way to open a font file, create a new bitmap image, and to draw the given character. This part of the code is very simple, all that we need is supported by the .NET framework. May be, the only thing to remember is that the Graphics
class can not be used with an indexed color, so create a Bitmap
object with default definitions.
public static Bitmap Convert(
string fontFileName,
byte[] charCode,
Encoding encoding,
Color color
)
{
PrivateFontCollection fonts = new
PrivateFontCollection();
fonts.AddFontFile( fontFileName );
char[] charToDraw = encoding.GetChars( charCode );
FontFamily family =
(FontFamily)fonts.Families.GetValue( 0 );
Bitmap bitmap = new Bitmap( 32, 32 );
Graphics g = Graphics.FromImage( bitmap );
g.DrawString(
new string( charToDraw ),
new Font( family, 12, FontStyle.Regular ),
new SolidBrush( color ),
6, 6
);
Color transColor = bitmap.GetPixel( 0, 0 );
bitmap.MakeTransparent( transColor );
return bitmap;
}
Step 2
Now, we need to convert a rich color bitmap image to a bitmap with an 8bpp format. For some reason, the method Bitmap.Save
doesn't really work for me. I mean, it's possible to save a bitmap, but it's impossible to change the color depth by passing EncoderParameter
. So changing the color depth is a little bit tricky. Since it's impossible to work with Graphics
because of the indexed color, we need to iterate all pixels in the source (rich color) bitmap image, get the pixel's color, and find a similar color from the Color
palette (256 Colors) and save its index in the destination bitmap image (8bpp). See the code below:
public static Bitmap ConvertTo8bppFormat(Bitmap image)
{
Bitmap destImage = new Bitmap(
image.Width,
image.Height,
PixelFormat.Format8bppIndexed
);
BitmapData bitmapData = destImage.LockBits(
new Rectangle( 0, 0, image.Width, image.Height ),
ImageLockMode.ReadWrite,
destImage.PixelFormat
);
for( int i = 0; i < image.Width; i++ )
{
for( int j = 0; j < image.Height; j++ )
{
Color color = image.GetPixel( i, j );
byte index = GetSimilarColor( destImage.Palette, color );
WriteBitmapData( i, j, index, bitmapData, 8 );
}
}
destImage.UnlockBits( bitmapData );
return destImage;
}
private static byte GetSimilarColor(ColorPalette palette, Color color)
{
byte minDiff = byte.MaxValue;
byte index = 0;
for( int i = 0; i < palette.Entries.Length-1; i++ )
{
byte currentDiff = GetMaxDiff( color, palette.Entries[i] );
if( currentDiff < minDiff )
{
minDiff = currentDiff;
index = (byte)i;
}
}
return index;
}
private static byte GetMaxDiff(Color a, Color b)
{
byte bDiff = System.Convert.ToByte(
Math.Abs( (short)( a.B - b.B ) ) );
byte gDiff = System.Convert.ToByte(
Math.Abs( ( short )( a.G - b.G ) ) );
byte rDiff = System.Convert.ToByte(
Math.Abs( ( short )( a.R - b.R ) ) );
return Math.Max( rDiff, Math.Max( bDiff, gDiff ) );
}
private static void WriteBitmapData(
int i,
int j,
byte index,
BitmapData data,
int pixelSize)
{
double entry = pixelSize/8;
IntPtr realByteAddr = new IntPtr( System.Convert.ToInt32(
data.Scan0.ToInt32() +
( j*data.Stride ) + i*entry ) );
byte[] dataToCopy = new byte[] { index };
Marshal.Copy( dataToCopy, 0, realByteAddr,
dataToCopy.Length );
}
Some more explanations ...
Some more explanations about the WriteBitmapData
method: the BitmapData
member Scan0
points to the first byte (physical memory that contains the pixel's information) and Stride
contains the row size. In our case (8 bit per pixel), finding the appropriate information for a given pixel is very easy (row index * row length + column index). So first, we create a pointer to the required byte, and then by using the Marshal
class, we can copy the color's index to the bitmap.
Enjoy - :)