Click here to Skip to main content
15,880,469 members
Articles / Programming Languages / C#
Article

How to convert a TTF character to an 8bpp BMP file

Rate me:
Please Sign up or sign in to vote.
4.29/5 (5 votes)
2 Nov 20062 min read 117.1K   34   19
This article describes how to convert a TTF file's character to BMP.

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.

C#
/// <summary>
/// Converts TTF character to bitmap
/// </summary>
/// <param name="fontFileName">
/// Full name of font file
/// </param>
/// <param name="charCode">Charcter code</param>
/// <param name="encoding">ASCII, UNICODE ...</param>
/// <param name="color">Color to draw given character</param>
/// <returns></returns>

public static Bitmap Convert(
    string fontFileName, 
    byte[] charCode, 
    Encoding encoding,
    Color color
    )
{

    // Create font 
    PrivateFontCollection fonts = new  
        PrivateFontCollection();
    fonts.AddFontFile( fontFileName );

    // Get charTodraw by given unicode
    char[] charToDraw = encoding.GetChars( charCode ); 

    // Get font family 
    FontFamily family = 
        (FontFamily)fonts.Families.GetValue( 0 );

    // Create bitmap 
    Bitmap bitmap = new Bitmap( 32, 32 );

    // Create graphics from image 
    Graphics g = Graphics.FromImage( bitmap );

    // Draw string 
    g.DrawString( 
        new string( charToDraw ), 
        new Font( family, 12, FontStyle.Regular ),
        new SolidBrush( color ),
        6, 6
        );

    // Set background to be transparent
    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:

C#
/// <summary>
/// Converts bitmap format to 8bpp
/// </summary>
/// <param name="image">Source bitmap image</param>
/// <returns></returns>

public static Bitmap ConvertTo8bppFormat(Bitmap image)
{

    // Create new image with 8BPP format
    Bitmap destImage = new Bitmap(
        image.Width,
        image.Height,
        PixelFormat.Format8bppIndexed
        );

    // Lock bitmap in memory
    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++ )
        {
            // Get source color
            Color color = image.GetPixel( i, j );

            // Get index of similar color 
            byte index = GetSimilarColor( destImage.Palette, color );

            WriteBitmapData( i, j, index, bitmapData, 8 );  
        }
    }

    destImage.UnlockBits( bitmapData );

    return destImage;
}

/// <summary>
/// Returns Similar color 
/// </summary>
/// <param name="palette"></param>
/// <param name="color"></param>
/// <returns></returns>

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

/// <summary>
/// Return similar color
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>

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

/// <summary>
/// Writing to bitmap
/// </summary>
/// <param name="i"></param>
/// <param name="j"></param>
/// <param name="index"></param>
/// <param name="data"></param>
/// <param name="pixelSize"></param>

private static void WriteBitmapData(
    int i,
    int j,
    byte index,
    BitmapData data,
    int pixelSize)
{

    double entry = pixelSize/8;

    // Get unmanaged address of needed byte
    IntPtr realByteAddr = new IntPtr( System.Convert.ToInt32( 
                          data.Scan0.ToInt32()  + 
                          ( j*data.Stride ) + i*entry ) );

    // Create array with data to copy
    byte[] dataToCopy = new byte[] { index };

    // Perfrom copy
    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 - :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Retired
Israel Israel
Name: Statz Dima
Fields of interest: software

Comments and Discussions

 
Questionconvert bmp to ttf Pin
luofei983-May-12 17:05
luofei983-May-12 17:05 
QuestionCould you please give us a zip file with a working demo? Pin
Eric Engler13-Apr-07 12:20
Eric Engler13-Apr-07 12:20 
AnswerRe: Could you please give us a zip file with a working demo? Pin
Dima Statz18-Apr-07 2:24
Dima Statz18-Apr-07 2:24 
GeneralRe: Could you please give us a zip file with a working demo? Pin
Eric Engler18-Apr-07 4:57
Eric Engler18-Apr-07 4:57 
AnswerRe: Could you please give us a zip file with a working demo? Pin
Dima Statz18-Apr-07 2:36
Dima Statz18-Apr-07 2:36 
QuestionHow to convert 8bpp BMP file to an 4bpp or 1bpp BMP file? Pin
ZhangPing06286-Feb-07 23:41
ZhangPing06286-Feb-07 23:41 
AnswerRe: How to convert 8bpp BMP file to an 4bpp or 1bpp BMP file? Pin
Dima Statz18-Apr-07 2:33
Dima Statz18-Apr-07 2:33 
GeneralBRAVO, too! [modified] Pin
J.Thomas3-Jan-07 7:03
J.Thomas3-Jan-07 7:03 
GeneralRe: BRAVO, too! Pin
Dima Statz4-Jan-07 7:23
Dima Statz4-Jan-07 7:23 
GeneralMemoryStream insufficient Pin
J.Thomas7-Jan-07 21:52
J.Thomas7-Jan-07 21:52 
GeneralRe: MemoryStream insufficient Pin
Dima Statz8-Jan-07 7:18
Dima Statz8-Jan-07 7:18 
GeneralRe: MemoryStream insufficient [modified] Pin
J.Thomas8-Jan-07 22:32
J.Thomas8-Jan-07 22:32 
Hi Dima,

I'm sorry; it looks like you misunderstood my previous message. My original bitmap is created by a (rather) full screen winform via form.DrawToBitmap or a similar method in NET 1.1. It uses roundabout 1050x760 pixel.

1. To print this bitmap directly, the print job contains 4 MB. Then I get a printer error "memory full - too many data".

2. To use your converting via memory stream (very quickly), the print job contains 2,25 MB. I get a printer error "memory full - too many data", too. I changed your code to the following:
private Bitmap ConvertTo8bpp(Bitmap bmp) {
    int iPixelSize = 4;
    MemoryStream str = new MemoryStream( bmp.Width * bmp.Height * iPixelSize );
    //  Save this bitmap to memory stream in gif format ( always 8 bit depth )
    bmp.Save( str, ImageFormat.Gif );
    //  load stored data once more
    return (Bitmap)Bitmap.FromStream(str);
}

3. To use your converting via step 2 of your article (inacceptable slowly), the print job contains 650 kB and can be printed.

My pursuit is a printable version that doesn't need too much time. I'm a pity that your converters do not so: ConvertTo8bppFormat() needs much too long; MemoryStream gives a not printable print job. I didn't say that the code in your previous message has an error, but both ways are not practicable for me.

Nevertheless, thanks for help! Juergen



-- modified at 11:34 Tuesday 9th January, 2007
GeneralRe: MemoryStream insufficient Pin
Dima Statz13-Jan-07 21:43
Dima Statz13-Jan-07 21:43 
GeneralRe: MemoryStream insufficient Pin
Dima Statz15-Jan-07 7:11
Dima Statz15-Jan-07 7:11 
QuestionIs this possible? Pin
whatever8422-Nov-06 3:13
whatever8422-Nov-06 3:13 
AnswerRe: Is this possible? Pin
Dima Statz23-Nov-06 4:48
Dima Statz23-Nov-06 4:48 
GeneralBRAVO! Pin
dArK cHAriSmA14-Nov-06 9:26
dArK cHAriSmA14-Nov-06 9:26 
GeneralRe: BRAVO! Pin
Dima Statz14-Nov-06 22:18
Dima Statz14-Nov-06 22:18 
GeneralRe: BRAVO! Pin
Dima Statz14-Nov-06 22:24
Dima Statz14-Nov-06 22:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.