Search code examples
c++winapiopenglglfwftgl

Difficulty Changing Text Color in FreeType and OpenGL Rendering


I'm currently facing challenges in changing the text color when rendering text using FTGL and OpenGL. Despite efforts to set the text color, the rendered text appears to be in a default color (red). I've tried adjusting the color parameters, but the results remain unchanged.

I've tried changing

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);

To the following and setting the following for the color

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

Here is the full code and also my rendering window. Thanks in advance!

void CreateTransparentWindow() {

    // Initialize GLFW
    if (!glfwInit()) {
        obs_log(LOG_INFO, "GlfwInit failed to initilize.");
        return false;
    }

    // Set window hints for borderless and transparent window
    glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
    glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);

    // 4x antialiasing - Makes things look sharper.
    glfwWindowHint(GLFW_SAMPLES, 4);
    
    // Create a windowed mode window and its OpenGL context
    window = glfwCreateWindow(1919, 1079, "Qt281QWindowIcon", NULL, NULL);
    if (!window) {
        obs_log(LOG_INFO, "Failed to create window.");
        glfwTerminate();
        return false;
    }

    // Make the window's context current
    glfwMakeContextCurrent(window);

    // Set up the projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 1919, 1079, 0, -1, 1); // Custom coordinates

    glMatrixMode(GL_MODELVIEW);

    glEnable(GL_MULTISAMPLE); // Anti antialiasing

    //Initilize FT Libary and Text Attributes.
    FT_Init_FreeType(&ft);
    FT_New_Face(ft, "C:\\Users\\Joel\\Desktop\\Roboto-Regular.ttf", 0, &face);
    glEnable(GL_TEXTURE_2D);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Get the HWND of the GLFW window
    HWND hwnd = glfwGetWin32Window(window);

    // Set the window to be click-through
    SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT);

    // Set the layered attribute to enable transparency
    SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);

    // Make the window always on top
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    // Set V-Sync to 1 (enabled)
    glfwSwapInterval(0);

    return true;

}

void DrawTextGL(int x, int y, int size, const char *text, COLORREF color)
{
    // Set font size and color
    FT_Set_Pixel_Sizes(face, 0, size);

    // Create and bind texture outside the loop
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);

    // Set texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    for (const char *c = text; *c; ++c) {
        if (FT_Load_Char(face, *c, FT_LOAD_RENDER) != 0) {
            continue;
        }

        // Copy glyph bitmap to texture
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);

        // Calculate texture coordinates
        float tx1 = x + face->glyph->bitmap_left;
        float ty1 = y - face->glyph->bitmap_top;
        float tx2 = tx1 + face->glyph->bitmap.width;
        float ty2 = ty1 + face->glyph->bitmap.rows;

        // Render textured quad
        glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0);
        glVertex2f(tx1, ty1);

        glTexCoord2f(1.0, 0.0);
        glVertex2f(tx2, ty1);

        glTexCoord2f(1.0, 1.0);
        glVertex2f(tx2, ty2);

        glTexCoord2f(0.0, 1.0);
        glVertex2f(tx1, ty2);
        glEnd();

        // Advance position
        x += (face->glyph->advance.x >> 6);
        y += (face->glyph->advance.y >> 6);
    }

    // Clean up: delete texture after the loop
    glDeleteTextures(1, &textureID);
}

Solution

  • Without shaders, GL_RED won't do what you want, as if fixes G/B/A channels as 0/0/1. Programmable shaders will let you repurpose the channel, but it appears you are using legacy OpenGL, which doesn't offer you that.

    To do what you want without programmable shaders, one solution is to change to using RGBA (or RGB), as you have begun doing. However, you will also need to take the single 8-bit channel data in face->glyph->bitmap.buffer and build a new RGBA buffer. You can then pass the RGBA buffer into glTexImage2D instead.

    Edit: If you don't mind the background of the text forced to black, you could alternately use internal format GL_RGBA, and format GL_LUMINANCE. This puts the value from the 8-bit source channel into each of the three color channels R/G/B and 1.0 in the alpha channel... in other words: opaque grayscale. (I thought GL_INTENSITY was supposed to copy it to all four channels, but it did not seem to work when I tested it.) If you run glPixelTransferf() on GL_RED_SCALE, GL_GREEN_SCALE, and GL_BLUE_SCALE before glTexImage2D(), you can change from grayscale to shades of another color.

    Edit 2: Similarly, you can create a black texture with alpha-mask using GL_RGBA/GL_ALPHA. The color can be adjusted to another color by setting GL_RED_BIAS, GL_GREEN_BIAS, and GL_BLUE_BIAS using glPixelTransferf() ahead of time. It'd also probably a good idea to set these back to 0 once you're done loading the glyph textures.