Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem rendering characters with Freetype and OpenGL

Tags:

c++

freetype

I've trying to develop a simple 2D game engine and I need to be able to draw text, so I decided to use the famous, freetype library. My current goal is to render every character from 0 to 255, and store the appropriate information in a glyph object, top and left positions of the character, width and height and an OpenGL texture. (In the future I wish to remove having 1 texture per glyph, but before starting to work on that I need my characters to be displaying correctly). First I got all characters garbled but then I checked out this question and figured out I had to copy the bitmap buffer into a new one with OpenGL required texture sizes(i.e. a power of two for both width and height). From there on, every character looked great, so I tried different font sizes to see if everything was working out OK, guess what...it wasn't, on small font sizes(i.e <30) every character with a small width ('l', 'i', ';', ':') showed up garbled.

http://img17.imageshack.us/img17/2963/helloworld.png

All distorted characters had a width of 1, which came out to two, because that's the first power of two, here's the code for rendering each character.

bool Font::Render(int PixelSize)
{
    if( FT_Set_Pixel_Sizes( mFace, 0, PixelSize ) != 0)
        return false;

    for(int i = 0; i < 256; ++i)
    {
        FT_UInt GlyphIndex;

        GlyphIndex = FT_Get_Char_Index( mFace, i );

        if( FT_Load_Glyph( mFace, GlyphIndex, FT_LOAD_RENDER ) != 0)
            return false;


        int BitmapWidth = mFace->glyph->bitmap.width;
        int BitmapHeight = mFace->glyph->bitmap.rows;

        int TextureWidth = NextP2(BitmapWidth);
        int TextureHeight= NextP2(BitmapHeight);

        printf("Glyph is %c, BW: %i, BH: %i, TW: %i, TH: %i\n", i, BitmapWidth, BitmapHeight, TextureWidth, TextureHeight);

        mGlyphs[i].SetAdvance(mFace->glyph->advance.x >> 6);
        mGlyphs[i].SetLeftTop(mFace->glyph->bitmap_left, mFace->glyph->bitmap_top);

        GLubyte * TextureBuffer = new GLubyte[ TextureWidth * TextureHeight ];

        for(int j = 0; j < TextureHeight; ++j)
        {
            for(int i = 0; i < TextureWidth; ++i)
            {
                TextureBuffer[ j*TextureWidth + i ] = (j >= BitmapHeight || i >= BitmapWidth ? 0 : mFace->glyph->bitmap.buffer[ j*BitmapWidth + i ]);
            }
        }

        for(int k = 0; k < TextureWidth * TextureHeight; ++k)
            printf("Buffer is %i\n", TextureBuffer[k]);

        Texture GlyphTexture;

        GLuint Handle;
        glGenTextures( 1, &Handle );

        GlyphTexture.SetHandle(Handle);
        GlyphTexture.SetWidth(TextureWidth);
        GlyphTexture.SetHeight(TextureHeight);

        glBindTexture(GL_TEXTURE_2D, Handle);

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TextureWidth, TextureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, TextureBuffer);

        mGlyphs[i].SetTexture(GlyphTexture);

        delete [] TextureBuffer;
    }

    return true;
}

The only solution I could find was a little if statement, changing the TextureWidth to 4 if it has the value of 2, and that way the text looks fine in every font size. But this doesn't make any sense to me, why would OpenGL reject width of 2 textures? Oh, and I've determined that the problem does not lie within the buffer, I printed it out and it looks just fine.

Can you think of any reason why this is happening?

Here's the rest of the (extremely) messy code, in case the problem does not lie within the Render function.

int NextP2(int Val)
{
    int RVal = 2;

 while(RVal < Val) RVal <<= 1;

 return RVal;
}

class Texture
{
    public:
        void Free() { glDeleteTextures(1, &mHandle); }

        void SetHandle(GLuint Handle) { mHandle = Handle; }
        void SetWidth(int Width) { mWidth = Width; }
        void SetHeight(int Height) { mHeight = Height; }

        inline GLuint Handle() const { return mHandle; }
        inline int Width() const { return mWidth; }
        inline int Height() const { return mHeight; }
    private:
        GLuint mHandle;
        int mWidth;
        int mHeight;
};

class Glyph
{
    public:

        void SetAdvance(int Advance) { mAdvance = Advance; }
        void SetLeftTop(int Left, int Top) { mLeft = Left; mTop = Top; }
        void SetTexture(Texture texture) { mTexture = texture; }

        Texture & GetTexture() { return mTexture; }
        int GetAdvance() const { return mAdvance; }
        int GetLeft() const { return mLeft; }
        int GetTop() const { return mTop; }

    private:
        int mAdvance;
        int mLeft;
        int mTop;
        Texture mTexture;

};

class Font
{
    public:
        ~Font() { for(int i = 0; i < 256; ++i) mGlyphs[i].GetTexture().Free(); }
        bool Load(const std::string & File);
        bool Render(int PixelSize);

        Glyph & GetGlyph(unsigned char CharCode) { return mGlyphs[CharCode]; }

    private:
        FT_Face mFace;
        Glyph mGlyphs[256];

};

I think that's about everything, please let me know if you've found the problem.

Best Regards and Thanks in Advance,

like image 698
Carlos Avatar asked Oct 27 '10 17:10

Carlos


1 Answers

I bet I stumbled upon exactly the same problem with freetype and OpenGL last week.

OpenGL default is to consider that texture data rows are aligned on 4 bytes boundaries. This causes problems when texture width becomes 2 or 1 which are valid power-of-2 sizes.

Does setting the row alignment to 1 byte boundaries fix your problem ? Using

glPixelStore(GL_UNPACK_ALIGNMENT, 1)

HTH

like image 59
rotoglup Avatar answered Nov 02 '22 20:11

rotoglup