I am generating a bitmap, however, I am not sure on how to render the UV's and placement. I had a thread like this once before, but it was too loosely worded as to what I was looking to do.

What I am doing right now is creating a large 1024x1024 image with characters evenly placed every 64 pixels. Here is an example of what I mean. I then save the bitmap X/Y information to a file (which is all multiples of 64).

However, I am not sure how to properly use this information and bitmap to render. This falls into two different categories, UV generation and kerning. Now I believe I know how to do both of these, however, when I attempt to couple them together I will get horrendous results.

Now for the algorithm. I am doing my letter placement with both GetABCWidth and GetKerningPairs. I am using GetABCWidth for the width of the characters, then I am getting the kerning information for adjust the characters.

Does anyone have any suggestions on how I can implement my own bitmap font renderer? I am trying to do this without using external libraries such as angel bitmap tool or freetype. I also want to stick to the way the bitmap font sheet is generated so I can do extra effects in the future.

Rendering Algorithm

for(U32 c = 0, vertexID = 0, i = 0; c < numberOfCharacters; ++c, vertexID += 4, i += 6) 

    ObtainCharInformation(fontName, m_Text[c]);

    letterWidth = (charInfo.A + charInfo.B + charInfo.C) * scale;

    if(c != 0)

        DWORD BytesReq = GetGlyphOutlineW(dc, m_Text[c], GGO_GRAY8_BITMAP, &gm, 0, 0, &mat);
        U8 * glyphImg= new U8[BytesReq];
        DWORD r = GetGlyphOutlineW(dc, m_Text[c], GGO_GRAY8_BITMAP, &gm, BytesReq, glyphImg, &mat);

        for (int k=0; k<nKerningPairs; k++)
            if ((kerningpairs[k].wFirst == previousCharIndex) && (kerningpairs[k].wSecond == m_Text[c])) {
                letterBottomLeftX += (kerningpairs[k].iKernAmount * scale);

        letterBottomLeftX -= (gm.gmCellIncX * scale);

    SetVertex(letterBottomLeftX, 0.0f, zFight, vertexID);
    SetVertex(letterBottomLeftX, letterHeight, zFight, vertexID + 1);
    SetVertex(letterBottomLeftX + letterWidth, letterHeight, zFight, vertexID + 2);
    SetVertex(letterBottomLeftX + letterWidth, 0.0f, zFight, vertexID + 3);
    zFight -= 0.001f;

    float BottomLeftX = (F32)(charInfo.bitmapXOrigin) / (float)m_BitmapWidth;
    float BottomLeftY = (F32)(charInfo.bitmapYOrigin + charInfo.charBitmapHeight) / (float)m_BitmapWidth;

    float TopLeftX = BottomLeftX;
    float TopLeftY = (F32)(charInfo.bitmapYOrigin) / (float)m_BitmapWidth;

    float TopRightX = (F32)(charInfo.bitmapXOrigin + charInfo.B - charInfo.C) / (float)m_BitmapWidth;
    float TopRightY = TopLeftY;

    float BottomRightX = TopRightX;
    float BottomRightY = BottomLeftY;

    SetTextureCoordinate(TopLeftX, TopLeftY, vertexID + 1);
    SetTextureCoordinate(BottomLeftX, BottomLeftY, vertexID + 0);
    SetTextureCoordinate(BottomRightX, BottomRightY, vertexID + 3);
    SetTextureCoordinate(TopRightX, TopRightY, vertexID + 2);

    /// index setting

    letterBottomLeftX += letterWidth;

    previousCharIndex = m_Text[c];


closed as too localized by Tetrad Aug 29 '11 at 15:24

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    \$\begingroup\$ It looks like you get either wrong character widths or kerning values. Did you try disabling the kerning altogether to check if you get correct character widths this way? \$\endgroup\$ – bummzack Jan 10 '11 at 9:18
  • \$\begingroup\$ Yes I did, I am not getting the correct character widths. how can i properly obtain those? \$\endgroup\$ – jack Jan 10 '11 at 16:06
  • \$\begingroup\$ Sorry, my comment was probably ambiguous. What I meant is: If you disable the kerning (eg. the whole if(c != 0) block). Do you still get these overlapping characters? \$\endgroup\$ – bummzack Jan 11 '11 at 8:12
  • 1
    \$\begingroup\$ Also, please don't ask the same question on Stack Overflow and here (stackoverflow.com/questions/4601217/…). \$\endgroup\$ – user744 Jan 11 '11 at 11:10
  • 1
    \$\begingroup\$ Justification for vote-to-close as too localized: The author hasn't been here since a week after the question, it's "fix my code" in a very specific case, not enough information is provided, and tools to solve the general problem are widely available and referenced in other answers. \$\endgroup\$ – user744 Mar 1 '11 at 23:35

Like bluescrn and Jari have suggested, I'd say go with bmfont if you can.

But if you want an example that just draws text, here's a snippet I was toying with some months back. Part are surely borrowed from somewhere (the PNG codec writer), but I have no idea where.

Good luck!

#include <Windows.h>
#include <Wingdi.h>
#include <gdiplus.h>
#include <Gdiplusgraphics.h>
using namespace Gdiplus;

extern int MyGenTexture[512*512];
int MyGenTexture[512*512];

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
         *pClsid = pImageCodecInfo[j].Clsid;
         return j;  // Success

   return -1;  // Failure

extern void MakeTexture();
void MakeTexture() {
    HDC myDC = CreateCompatibleDC(NULL);
    //HBITMAP GenTexture = CreateCompatibleBitmap( Context, 512, 512 );

    BITMAPINFOHEADER myInfoHeader = {
        -512, // Negative, to make it top down //
        0, // Size of compressed data //
        4096, // Pixels per meter //

/*  HBITMAP myTexture = CreateDIBitmap(

    BITMAPINFO myInfo = {

    int* Data;

    HBITMAP myTexture = CreateDIBSection(

    SelectObject( myDC, myTexture );

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;
    // Initialize GDI+.
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

        Graphics g( myDC, myTexture );
        SolidBrush brush( Color(255,255,255,255) ); 
        FontFamily fontFamily( L"Times New Roman" );
        Font font( &fontFamily, 48, FontStyleRegular, UnitPixel );

        for( int idx = 0; idx < 512*512; idx++ ) {
            Data[idx] = 0x00FFFFFF;

        //g.Clear( Color(0,255,255,255) );
        g.DrawString(L"Using \"Technology\", i.e.",-1,&font,PointF(0,20),&brush);
        g.DrawString(L"GDI+, I have created a",-1,&font,PointF(0,70),&brush);
        g.DrawString(L"texture from system",-1,&font,PointF(0,120),&brush);
        g.DrawString(L"installed fonts! That",-1,&font,PointF(0,170),&brush);
        g.DrawString(L"means international",-1,&font,PointF(0,220),&brush);
        g.DrawString(L"      スマイルズ ",-1,&font,PointF(0,370),&brush);

        for( int idx = 0; idx < 512*512; idx++ ) {
            MyGenTexture[idx] = Data[idx];

        // Write File //
        Bitmap OutBitmap(myTexture,NULL);

        CLSID encoderClsid;
        GetEncoderClsid(L"image/png", &encoderClsid);
        Status stat = OutBitmap.Save(L"Awesome.png", &encoderClsid, NULL);


    DeleteObject( myTexture );

You could always give this a try: http://www.angelcode.com/products/bmfont/

It generates nicely-packed textures of bitmap characters, and an .xml file of all the UV/spacing data. Results are generally very good.

  • 2
    \$\begingroup\$ He explicitly says "I am trying to do this without using external libraries such as angel bitmap tool or freetype.", but yes, I would also recommend using the angelcode bitmap font tool. =) \$\endgroup\$ – Jari Komppa Jan 11 '11 at 11:18