Surface poorly filled with sdl_ttf

1

I'm trying to make an openGL game in c++ and I'm trying to implement a text system, to do this I'm trying to use SDL_ttf.

I already used SDL_ttf in an other project but with another api, so I made the same code but it happened to not fill the pixel data of the surface.

Here is my code :


void Text2Texture::setText(const char * text, size_t fontIndex){

    SDL_Color c = {255, 255, 0, 255};
    SDL_Surface * surface;

    surface = TTF_RenderUTF8_Blended(loadedFonts_[fontIndex], text, c);
    if(surface == nullptr) {
        fprintf(stderr, "Error TTF_RenderText\n");
        return;
    }    

    GLenum texture_format;
    GLint colors = surface->format->BytesPerPixel;

    if (colors == 4) {   // alpha
        if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGBA;
        else
        texture_format = GL_BGRA_EXT;
    } else {             // no alpha
        if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGB;
        else
        texture_format = GL_BGR_EXT;
    }


    glBindTexture(GL_TEXTURE_2D, textureId_);
    glTexImage2D(GL_TEXTURE_2D, 0, colors, surface->w, surface->h, 0, texture_format, GL_UNSIGNED_BYTE, surface->pixels);

    ///This line tell me pixel data is 8 bit witch isn't good ?
    std::cout << "pixel size : " << sizeof(surface->pixels) << std::endl;

    ///This line give me correct result
    fprintf(stderr, "texture size : %d %d\n", surface->w, surface->h);

    glBindTexture(GL_TEXTURE_2D, 0);

}

As you can see in the comment, the pointer pixels in surface have a size of 8 bit, witch is way too low for a texture. I don't know why It do that.

At the end, the texture data look to be fully filled with 0 (resulting with a black squad using very basic shaders).

In this project I'm using glfw to create an openGL context so I'm not using sdl and I did not initialized it.

However, I did initialize sdl_ttf, here is all I did before calling setText :


std::vector<TTF_Font *> Text2Texture::loadedFonts_;

void Text2Texture::init(){

    if(TTF_Init() == -1) {
        fprintf(stderr, "TTF_Init: %s\n", TTF_GetError());
    }

}

int Text2Texture::loadFont(std::string const& fontPath){

    loadedFonts_.emplace_back();

    loadedFonts_.back() = TTF_OpenFont(fontPath.data(), 32);

    if( loadedFonts_.back() == nullptr ) {
        fprintf(stderr, "TTF_OpenFont: %s \n", TTF_GetError());
        loadedFonts_.pop_back();
        return -1;
    }

    return ((int)loadedFonts_.size() - 1);

}

///The constructor initialize the texture :

Text2Texture::Text2Texture(){

    glGenTextures(1, &textureId_);
    glBindTexture(GL_TEXTURE_2D, textureId_);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

}

My class got a static part here is it corp :


class Text2Texture {

public:

    Text2Texture();

    void setText(const char * text, size_t fontIndex = 0);

    unsigned int getId() const;

    //Partie static
    static void init();

    static void quit();

    static int loadFont(std::string const& fontPath);

private:

    unsigned int textureId_;

    //Partie static
    static std::vector<TTF_Font *> loadedFonts_;

};

I initialize sdl_ttf and load texture with static method then I create class instance to create specific texture.

If you find where is my mistake I would be pleased to read your answer.

(By the way, I'm not really sure using sdl_ttf is the good approach, if you have a better idea I would take it too but I would like to solve this problem first)

c++
opengl
textures
sdl-ttf
asked on Stack Overflow Sep 14, 2019 by Jean Mathieu • edited Sep 15, 2019 by Rabbid76

1 Answer

0

The format and type parameter of glTexImage2Dspecifiy how e single pixel is encoded.
When the texture font is created, then each pixel is encoded to a single byte. This means your texture consist of a single color channel and each pixel has 1 byte. Im very sure that colors = surface->format->BytesPerPixel is 1. Note that it is completely sufficient to encode the glyph in one color channel, because there glyph just consists of a information.

By default OpenGL assumes that the start of each row of an image is aligned 4 bytes. This is because the GL_UNPACK_ALIGNMENT parameter by default is 4. Since the image has 1 (RED) color channel, and is tightly packed the start of a row is possibly misaligned.
Change the the GL_UNPACK_ALIGNMENT parameter to 1, before specifying the two-dimensional texture image (glTexImage2D).

Since the texture has only one (red) color channel, the green an blue color will be 0 and the alpha channel will be 1 when the texture is looked up. But you can treat green, blue and even the alpha channel to be read from the red color channel, too. This can be achieved by setting the texture swizzle parameters GL_TEXTURE_SWIZZLE_G, GL_TEXTURE_SWIZZLE_B respectively GL_TEXTURE_SWIZZLE_A. See glTexParameter.

Further not that the texture parameter are stored in the texture object. glTexParameter changes the texture object which is currently bound to the specified targetof the current texture unit. So it is sufficient to set the parameters once when the texture image is created.
In compare glPixelStore changes global states an ma have to be set to its default value after specifying the texture image (if later calls to glTexImage2D rely on it).

The specification of the 2 dimensional texture image and setting the parameters may look as follows:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, surface->w, surface->h, 0,
             GL_RED, GL_UNSIGNED_BYTE, surface->pixels);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
answered on Stack Overflow Sep 15, 2019 by Rabbid76

User contributions licensed under CC BY-SA 3.0