7

I want to render an anti-aliased string on an SDL_Surface with a given alpha channel.

I figured out it is possible to render:

  • an anti-aliased string with the Blended variant of the string render method (ie: TTR_RenderText_Blended). But then I can't make it transparent.
  • An anti-aliased string with the Shaded method. But then there is a solid background. The background and the drawn string can be made transparent, but then the solid background is still there. Passing it a transparent background color is also not possible.
  • an non-anti-aliased string, which I can make transparent like I want with the Solid variant. But it is not anti-aliased.

Thanks

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • Did you ever get this to work? – Malabarba Oct 20 '11 at 21:38
  • @BruceConnor: No. That's indeed pretty sad about it. What I did was changing to OpenGL and using a bitmap font. This requires a lot of work, but when I finally got it working I was really very satisfied. Hopefully you are ready to spend some hours of programming to do the same. – Martijn Courteaux Oct 21 '11 at 14:56
  • Greetings, I'm having the same problem as you and haven't solved it yet. Maybe you'd have an idea by now, I asked a question there : http://stackoverflow.com/questions/12700085. In my case, I cannot use bitmap font for now. – ForceMagic Oct 02 '12 at 23:56
  • @ForceMagic: No, I have never solved it. I moved on to the real stuff, which is working with the GPU, instead of the CPU. Learn OpenGL, it is much more powerful. Once you know how to use it, you will be glad you learned. – Martijn Courteaux Oct 03 '12 at 18:32
  • @MartijnCourteaux Thanks for the advice, I'm currently using both, I use some SDL to make my life easier with the mouse input and stuff like that, but I do use OpenGL to draw most of my textures and tile system on the screen. However, I thought SDL_ttf could save me some time on the Text Drawing part. I was basically trying to drawn the generated texture from SDL_ttf with OpenGL. The mix between both of them doesn't seems very effective though. – ForceMagic Oct 03 '12 at 18:57
  • @ForceMagic: No, indeed. It's not a good idea to draw on top of a OpenGL buffer using SDL. Go for bitmap fonts, it is really a pain in the ass to make it work, but they are quite fast (much faster than SDL_ttf). – Martijn Courteaux Oct 03 '12 at 19:39
  • On the contrary, I think SDL_ttf works great with OpenGL. Once you have a function to convert a surface to a GL texture, you can render it on a quad, no problem (with alpha-blending). There's really no difference between a TTF- and a bitmap-rendered font if they're stored in textures. – Daniel Hanrahan Oct 08 '12 at 03:34

4 Answers4

13

I know I'm a bit late on this one :/

According to SDL documentation on SDL_SetAlpha:

Note that per-pixel and per-surface alpha cannot be combined; the per-pixel alpha is always used if available.

So regular SDL_BlitSurface/SDL_SetAlpha won't work here. But it can be done:

Example using only SDL

The only ways I can think of alpha-blending TTF_RenderText_Blended output are to use OpenGL, or adjust the alpha values of each pixel in the surface.

By adjusting per-pixel alpha values

You can do this by scaling the per-pixel alpha values from [0, 255] to a new range [0, alpha]:

// Changes a surface's alpha value, by altering per-pixel alpha if necessary.
void SetSurfaceAlpha (SDL_Surface *surface, Uint8 alpha)
{
    SDL_PixelFormat* fmt = surface->format;

    // If surface has no alpha channel, just set the surface alpha.
    if( fmt->Amask == 0 ) {
        SDL_SetAlpha( surface, SDL_SRCALPHA, alpha );
    }
    // Else change the alpha of each pixel.
    else {
        unsigned bpp = fmt->BytesPerPixel;
        // Scaling factor to clamp alpha to [0, alpha].
        float scale = alpha / 255.0f;

        SDL_LockSurface(surface);

        for (int y = 0; y < surface->h; ++y) 
        for (int x = 0; x < surface->w; ++x) {
            // Get a pointer to the current pixel.
            Uint32* pixel_ptr = (Uint32 *)( 
                    (Uint8 *)surface->pixels
                    + y * surface->pitch
                    + x * bpp
                    );

            // Get the old pixel components.
            Uint8 r, g, b, a;
            SDL_GetRGBA( *pixel_ptr, fmt, &r, &g, &b, &a );

            // Set the pixel with the new alpha.
            *pixel_ptr = SDL_MapRGBA( fmt, r, g, b, scale * a );
        }   

        SDL_UnlockSurface(surface);
    }       
}           

I know it looks scary, but it's pretty straight-forward. The key line is here:

*pixel_ptr = SDL_MapRGBA( fmt, r, g, b, scale * a );

You can use it like this:

text_surface = TTF_RenderText_Blended( font, "Hello World!", color );
SetSurfaceAlpha( text_surface, 128 );

With OpenGL

If using OpenGL, things are lot easier. Assuming you convert the SDL_Surface from TTF_RenderText_Blended to a GL texture, you can just use:

glColor4f( 1.0, 1.0, 1.0, Alpha );

before you render it on a textured quad.

But don't forget to enable alpha blending first!

glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
Daniel Hanrahan
  • 4,801
  • 7
  • 31
  • 44
  • Thank you for taking the time to provide this late answer. You may earn a Necromancer badge with it when some more people vote it up. – ypnos Oct 07 '12 at 21:53
  • Additionally, if you're using the OpenGL programmable pipeline, within the fragment shader you can multiply the texture unit by the alpha value. color = myColor.a * texture(myTexture, myCoords); – JoshBramlett Apr 30 '16 at 06:01
1

All you have to do is call SDL_SetAlpha on your SDL_Surface after creating the sprite on which the text is rendered, but before calling SDL_DisplayFormatAlpha on that sprite.

// Make the sprite with the text on it
SDL_Surface *swSprite = TTF_RenderText_Solid( font, text, textColor ) ;

// CALL SET ALPHA NOW
SDL_SetAlpha( swSprite, SDL_SRCALPHA, 128 ) ; // 50% opacity

// OK, NOW you can convert it to display format.  I'm presuming
// you called `SDL_SetVideoMode` with the `SDL_HWSURFACE` flag set previously
SDL_Surface* hwSprite = SDL_DisplayFormatAlpha( swSprite ) ;

// If you invert the above 2 steps, it won't work.

// We don't need the software sprite anymore
SDL_FreeSurface( swSprite ) ;

// Now draw the hwSprite as normal
SDL_BlitSurface( hwSprite, NULL, screen, &spriteLocation );
bobobobo
  • 64,917
  • 62
  • 258
  • 363
1

The way to do this with a TTF is to use SDL_SetTextureAlphaMod() Something like this

SDL_Surface *surface;
SDL_Texture *texture;
SDL_Color color = {255, 255, 255, 128}
surface = TTF_RenderText_Blended_Wrapped(myFont, "HI!", color, 100);
// myFont and the _renderer is pre-defined somewhere in the game
texture = SDL_CreateTextureFromSurface(_renderer, surface);
SDL_SetTextureAlphaMod(texture, color.a);
SDL_RenderCopy(_renderer, texture, NULL, &dest);
SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);

/*...*/

SDL_RenderPresent(_renderer);

https://wiki.libsdl.org/SDL_SetTextureAlphaMod

Lord Wolfenstein
  • 261
  • 2
  • 14
  • hooray, thanks for the answer! I wonder if this is new since question was originally posted? But this definitely worked for me. +1 – NHDaly Aug 20 '18 at 14:28
-2

Why not use bitmapped fonts? You could build a png image with alpha channel. I think that SDL_ttf works with the same system, it builds an image an internally uses bitmapped fonts.

ForceMagic
  • 6,230
  • 12
  • 66
  • 88
Victor Marzo
  • 713
  • 5
  • 10
  • 1
    Note that @ForceMagic has removed a dead link. If you can find a replacement please add it back in. To ForceMagic, since you asked, removing the dead link is fine, but please add a comment (which will notify the poster) so that they might get the notification and find a new (or replacement) link. – Servy Oct 03 '12 at 19:09