Bitmap font rendering, UV generation and vertex placement

jack
  • Bitmap font rendering, UV generation and vertex placement jack

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

  • 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.

  • 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;
             free(pImageCodecInfo);
             return j;  // Success
          }    
       }
    
       free(pImageCodecInfo);
       return -1;  // Failure
    }
    
    extern void MakeTexture();
    void MakeTexture() {
        HDC myDC = CreateCompatibleDC(NULL);
        //HBITMAP GenTexture = CreateCompatibleBitmap( Context, 512, 512 );
    
        BITMAPINFOHEADER myInfoHeader = {
            sizeof(BITMAPINFOHEADER),
            512,
            -512, // Negative, to make it top down //
            1,
            32,
            BI_RGB,
            0, // Size of compressed data //
            4096, // Pixels per meter //
            4096,
            0,
            0
            };
    
    /*  HBITMAP myTexture = CreateDIBitmap(
            myDC,
            &myInfoHeader,
            NULL,
            NULL,
            NULL,
            DIB_RGB_COLORS
            );
    */
    
        BITMAPINFO myInfo = {
            myInfoHeader,
            0
            };
    
        int* Data;
    
        HBITMAP myTexture = CreateDIBSection(
            myDC,
            &myInfo,
            DIB_RGB_COLORS,
            (void**)&Data,
            NULL,
            NULL
            );
    
        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"characters!",-1,&font,PointF(0,270),&brush);
            g.DrawString(L"",-1,&font,PointF(0,320),&brush);
            g.DrawString(L"      スマイルズ ",-1,&font,PointF(0,370),&brush);
            g.Flush();
    
    
            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);
        }
    
        GdiplusShutdown(gdiplusToken);
    
    
        DeleteObject( myTexture );
        //DeleteObject(GenTexture);
        DeleteDC(myDC);
    }
    

Tags
c++ graphics c# algorithm rendering
Related questions and answers
  • () will return a vector3 that has a 0 z component and x and y values from about 2 to roughly 33. Now how on earth can me with a calculator be better than the computer? what am I missing? 0.0f / 720.0f) * 2.0f - 1.0f should clearly = -1, not 1.2! what is going on? 0 / anything = 0, 0 * 2 = 0, 0 - 1 = -1!!! ... = xyCoords.y; m_font->DrawTextA(NULL, stateStream.str().c_str(), -1, &amp;rct, DT_LEFT|DT_NOCLIP , D3DXCOLOR(1,0,0,1)); This all works perfectly. Where am I going wrong with the first bit of code

  • (); // create the index buffer out of DWORDs DWORD OurIndices[] = { 0, 1, 2, // side 1 2, 1, 3, 4, 0, 6, // side 2 6, 0, 2, 7, 5, 6, // side 3 6, 5, 4, 3, 1, 7, // side 4... memcpy(pVoid, OurVertices, sizeof(OurVertices)); // copy the vertices to the buffer pBuffer->Unmap(); // create the index buffer out of DWORDs DWORD OurIndices[] = { 0, 1, 2, // side 1 2, 1, 3, 4, 0, 6, // side 2 6, 0, 2, 7, 5, 6, // side 3 6, 5, 4, 3, 1, 7, // side 4 7, 1, 5, 4, 5, 0, // side 5 0, 5, 1, 3, 7, 2, // side 6 2

  • I am quite new to OpenGL, I have managed after long trial and error to integrate Nehe's Cel-Shading rendering with my Model loaders, and have them drawn using the Toon shade and outline..., In); // Get The Current Line ( NEW ) shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = float(atof (Line)); // Copy Over The Value ( NEW ) } fclose... (1, &amp;shaderTexture[0]); // Get A Free Texture ID ( NEW ) glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // Bind This Texture. From Now On It Will Be 1D

  • ;&amp; ballPrev.coords[2] < block[i].coords[0] &amp;&amp; (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3]))) { left = 1; } if (ballPrev.coords[0] > block[i].coords[2] &amp;&amp; ballPrev.coords[2] > block[i].coords[2] &amp;&amp...I am trying to create a 2D platformer (Mario-type) game and I am some having some issues with handling collisions properly. I am writing this game in C++, using SDL for input, image loading, font

  • this on what I had done there, but modifying it to fit with Ogre. It is working but not correctly and I would like some help to understand what it is I am doing wrong. I have a state system and when... physics properties. I know there will be errors here as I get the items colliding with the ground but not with each other properly. So I would appreciate some input on what I am doing wrong. void...)Point[0], (float)Point[1], (float)Point[2])); // set rotation btVector3 EulerRotation; QuaternionToEuler(TObject->getOrientation(), EulerRotation); node->setOrientation(1,(Ogre

  • /albums/c308/thentsc/font_test.jpg The code looks something like this... for (uint8 c = 0; c < 127 - 32; c++) { str[0] = c + 32; SIZE size; GetTextExtentPoint32A(hDC, str, 1, &amp;size); if (x...() { g_TextObject->Render(); } So in general I guess my question is what is the correct approach for this issue? Should I just let each text object have a texture and on text object creation use...Update: The completed solution using Nathan Reed's answer is posted in my answer below A few open source programs I've seen that render installed fonts do something like this... Create a texture

  • : float m_Position[3]; // x, y, z // offset 0, size = 3*sizeof(float) float m_TexCoords[2]; // u, v // offset 3*sizeof(float), size = 2*sizeof(float) float m_Normal[3... onscreen, in the shape of the terrain rectangle, but there are no regular lines etc. Here's the code I use for rendering: void ShaderProgram::Draw() { using namespace AntiMatter; if( ! m... in the shader code) for( int n = 0; n < vShaderArgs.size(); n ++) glBindAttribLocation( m_nProgramId, n, vShaderArgs[n].sFieldName.c_str() ); // Create and bind to a vertex array object, which

  • I am making a game using OpenGL, with SDL_Image employed to load the textures. When drawn on screen, there's no alpha -- in an exact square around the opaque contents of the texture, black is drawn; outside that space, white is drawn. Here is my OpenGL init code... SDL_SetVideoMode(windowWidth, windowHeight, 16, SDL_HWSURFACE|SDL_GL_DOUBLEBUFFER|SDL_OPENGL); glClearColor(0, 0, 0, 0...; glGenTextures(1, &amp;thisIsMyTexture); temporarySurface = loadImage("theimagetomytexture.png"); glBindTexture(GL_TEXTURE_2D,thisIsMyTexture); glTexImage2D(GL_TEXTURE_2D, 0, 4, temporarySurface->w

  • collided with are deleted. However I am having trouble getting the INITIAL COLLISION with the wall to be detected correctly. the result is the playe incorrectly losing 4 lives after hitting 1 wall...; I< numb_coll &amp;&amp; numb_coll< player.get_location()[1]; I++){ theScreen.Insert(' ',player.get_location()[0], player.get_location()[1]-(I+1));} if(numb...}; // for the old/new screen command // Some nCurses setup int r = 0, c = 0; // current row and column (upper-left is (0,0)) const int nrows = 56, // number of rows in window ncols = 79

Data information