开发者

Problem rendering characters with Freetype and OpenGL

开发者 https://www.devze.com 2023-01-22 10:09 出处:网络
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.

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,


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


Prepare yourself to support >256 characters (That is history) and Right-to-left and left-to-right text flow as well as topdown flow.

Just a different topic hint for english speakers.


You probably ran into a texture-wrapping issue.

The pixels rendered on one border of your texture 'bleed' over to the other side in OpenGls attempt to tile the texture. correctly. If i am correct, changing you wrapping parameters to

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

should make opengl clamp your texture at its edges, and draw your small textures correctly.

Update

I found something on a tutorial page:

If a small font's bitmaps are not aligned with the screen pixels, bilinear interpolation will cause artifacts. An easy fix for this is to change the interpolation method from GL_LINEAR to GL_NEAREST, though this may lead to additional artifacts in the case of rotated text. (If rotating text is important to you, you may want to try telling OpenGL to super sample from larger font bitmaps.)


Use Freetype with ICU. Freetype alone can't render the glyphs properly. Use ICU for that.

0

精彩评论

暂无评论...
验证码 换一张
取 消