Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SDL + SDL_ttf: Transparent blended text?

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

like image 781
Martijn Courteaux Avatar asked Jul 12 '10 14:07

Martijn Courteaux


2 Answers

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 );
like image 113
Daniel Hanrahan Avatar answered Sep 21 '22 09:09

Daniel Hanrahan


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 );
like image 21
bobobobo Avatar answered Sep 23 '22 09:09

bobobobo