0
\$\begingroup\$

I'm trying to create a single SDL_Surface that has multiple lines of texts stacked on top of each other using SDL2.

When I create a single SDL_Surface for a text by using:

SDL_Surface * loadedSurface = TTF_RenderText_Blended(
            font,
            text,
            *(reinterpret_cast<const SDL_Color *>(&color.rgba)));

outTexture = SDL_CreateTextureFromSurface(_renderer, loadedSurface);

the result is a text texture that looks nice and smooth. Here is a visual representation of it:

enter image description here

However, I need to create an empty surface that has many texts like this "Device Settings" text on top of each other. What I do is:

  • I create an empty surface (res_surface) with the precalculated size needed for all text lines

  • Then I create said text lines the same way as before using

text_lines_surfaces[i] = TTF_RenderText_Blended(
            font,
            line_text,
            *(reinterpret_cast<const SDL_Color *>(&color.rgba)));
  • After that I use:

SDL_BlitSurface(text_lines_surfaces[i], NULL, res_surface, &line_rect);

Sadly, blitting a text surface on top of an empty one creates some strange outlines around the text surface. Here's an image of the issue (using a single text line here for simplicity):

enter image description here

Here is the simplified (using a single text line) code for this:

SDL_Surface * textLineSurface = TTF_RenderText_Blended(
            font,
            text_lines[0],
            *(reinterpret_cast<const SDL_Color *>(&color.rgba)));

SDL_Surface * resSurface = nullptr;

*outTextWidth  = textLineSurface->w;
*outTextHeight = textLineSurface->h;

// creates an empty rgb surface
createEmptySurface(*outTextWidth, *outTextHeight, resSurface);

SDL_Rect rct = {0, 0, 0, 0};
SDL_BlitSurface(loadedSurface, NULL, resSurface, &rct);

outTexture = SDL_CreateTextureFromSurface(_renderer, resSurface);

In case you need the createEmptySurface() function body:

int32_t Texture::createEmptySurface(const int32_t  width,
                                    const int32_t  height,
                                    SDL_Surface *& outSurface)
{
    int32_t err = EXIT_SUCCESS;

    if(nullptr != outSurface)
    {
        LOGERR("Warning, outSurface is not empty. Will not create Empty "
               "Surface. Memory leak prevented.");

        err = EXIT_FAILURE;
    }

    if(EXIT_SUCCESS == err)
    {
        /** If the depth is 4 or 8 bits, an empty palette will be allocated
          * for the surface.
          * If the depth is greater than 8 bits, the pixel format is set
          * using the flags '[RGB]/[RGBA] mask'.
          * */
        const uint32_t DEPTH = 32;

        /** SDL interprets each pixel as a 32-bit number, so masks must depend
          * on the endianness (byte order) of the machine
          * */
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
        const uint32_t RED_MASK   = 0xFF000000;
        const uint32_t GREEN_MASK = 0x00FF0000;
        const uint32_t BLUE_MASK  = 0x0000FF00;
        const uint32_t ALPHA_MASK = 0x000000FF;
#else
        const uint32_t RED_MASK   = 0xFF000000;
        const uint32_t GREEN_MASK = 0x00FF0000;
        const uint32_t BLUE_MASK  = 0x0000FF00;
        const uint32_t ALPHA_MASK = 0x000000FF;
#endif /* SDL_BYTEORDER == SDL_BIG_ENDIAN */

        outSurface = SDL_CreateRGBSurface(0,           //deprecated SDL param
                                          width,       //surface width
                                          height,      //surface height
                                          DEPTH,       //surface depth (using)
                                          RED_MASK,    //surface red mask
                                          GREEN_MASK,  //surface green mask
                                          BLUE_MASK,   //surface blue mask
                                          ALPHA_MASK); //surface alpha mask

        if(nullptr == outSurface)
        {
            LOGERR("SDL_CreateRGBSurface() failed: %s", SDL_GetError());

            err = EXIT_FAILURE;
        }
    }

    return err;
}

Only difference between the both ways of rendering the text SDL_Surface is that the first time it is created by only using TTF_RenderText_Blended and the second time this same thing is blitted on top of an empty SDL_Surface. What am I doing wrong here? How do I get rid of those text outlines using the second way?

\$\endgroup\$
  • \$\begingroup\$ This might have something to do with mismatched surface types. Have you tried rendering the text with different colors and comparing the output color channels to see if your channel masks match what you get from TTF_RenderText_Blended? Alternatively you could try creating the empty surface with the format of the text surface? \$\endgroup\$ – Tyyppi_77 Aug 7 at 14:28
  • \$\begingroup\$ @Tyyppi_77 not sure if that's what you meant but when creating the empty resulting surface with SDL_CreateRGBSurface by using the Rmask, Gmask, Bmask, Amask of the text surface's pixel format the result is the same disappointing outline. Changing the color of the text has no effect on the issue too. Texts in other colors have the same outline. \$\endgroup\$ – Stoyan Lupov Aug 7 at 15:00
  • \$\begingroup\$ Okay, have you experimented with different blend modes on the surfaces? \$\endgroup\$ – Tyyppi_77 Aug 7 at 15:57
  • \$\begingroup\$ @Tyyppi_77 ok, your suggestion was spot on! The created text's surface has a blendmode BLENDED which i did not know. I must play around with combination of some blendmodes for the different surfaces that are drawn on top of each other to get the result i need. Thanks! \$\endgroup\$ – Stoyan Lupov Aug 8 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.