SDL + SDL_ttf:透明混合文本?

7
我想在给定 Alpha 通道的 SDL_Surface 上呈现一条抗锯齿的字符串。
我发现可以使用字符串渲染方法的 Blended 变体(即 TTR_RenderText_Blended)来呈现抗锯齿字符串。但这样就无法使其透明。
也可以使用 Shaded 方法呈现抗锯齿字符串。但是会有一个实心背景。虽然可以将背景和绘制的字符串设置为透明,但实心背景仍会存在。也无法将透明背景颜色传递给它。
还可以呈现非抗锯齿字符串,我可以使用变体 Solid 并按照需要使其透明。但这样不会抗锯齿。
谢谢

你最终解决了这个问题吗? - Malabarba
@BruceConnor:不,这确实很遗憾。我所做的是改用OpenGL并使用位图字体。这需要大量的工作,但当我最终让它正常运行时,我真的非常满意。希望你也准备好花费一些时间来编程完成同样的事情。 - Martijn Courteaux
你好,我和你一样遇到了同样的问题,但是还没有解决。也许你现在有什么想法,我在这里提出了一个问题:https://dev59.com/NGnWa4cB1Zd3GeqPxSFp。在我的情况下,我现在不能使用位图字体。 - ForceMagic
@ForceMagic:不,我从来没有解决过这个问题。我转而去处理真正的东西,也就是与GPU一起工作,而不是CPU。学习OpenGL,它更加强大。一旦你知道如何使用它,你会很高兴你学了它。 - Martijn Courteaux
@MartijnCourteaux 谢谢你的建议,我目前正在同时使用它们。我使用一些SDL来方便地进行鼠标输入和其他操作,但是我确实使用OpenGL在屏幕上绘制大部分纹理和图块系统。然而,我认为SDL_ttf可以节省我的文本绘制时间。我基本上尝试使用OpenGL从SDL_ttf生成的纹理中绘制。不过两者的混合似乎效果不太好。 - ForceMagic
显示剩余2条评论
4个回答

13

我知道我有点晚了 :/

根据SDL关于SDL_SetAlpha的文档:

请注意,无法组合每个像素和每个表面的alpha;如果可用,则始终使用每个像素的alpha。

所以常规的SDL_BlitSurface/SDL_SetAlpha在这里行不通。但是它是可以实现的:

Example using only SDL

我能想到的唯一方法是使用OpenGL或调整表面中每个像素的alpha值来混合TTF_RenderText_Blended输出。

通过调整每个像素的alpha值

你可以通过将每个像素的alpha值从[0, 255]缩放到新范围[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);
    }       
}           

我知道它看起来很可怕,但它其实很简单。关键代码在这里:

*pixel_ptr = SDL_MapRGBA( fmt, r, g, b, scale * a );
你可以像这样使用它:
text_surface = TTF_RenderText_Blended( font, "Hello World!", color );
SetSurfaceAlpha( text_surface, 128 );

使用OpenGL

如果使用OpenGL,事情会变得简单许多。假设你将从TTF_RenderText_Blended转换而来的SDL_Surface转换为GL纹理,你只需要使用:

glColor4f( 1.0, 1.0, 1.0, Alpha );

在将其渲染到纹理四边形之前,确保启用 alpha 混合。

但是不要忘记先启用 alpha 混合!

glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

感谢您抽出时间提供这个晚回答。如果有更多人投票支持,您可能会获得一个“死灵法师”徽章。 - ypnos
此外,如果您正在使用OpenGL可编程管线,在片段着色器中,您可以通过alpha值将纹理单元乘以颜色。color = myColor.a * texture(myTexture, myCoords); - JoshBramlett

2
使用TTF字体实现这个的方法是使用SDL_SetTextureAlphaMod()。类似这样:
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


太好了,感谢你的回答!我想知道这个方法是不是在问题最初发布后才出现的?但是这个方法对我来说绝对有效。+1 - NHDaly

1
你所需要做的就是在创建渲染文本的精灵之后,但在调用SDL_DisplayFormatAlpha之前,在你的SDL_Surface上调用SDL_SetAlpha
// 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 );

请注意,这仅适用于“TTF_RenderText_Solid”变体,而不是“TTF_RenderText_Blended”。 - Daniel Hanrahan

-2

为什么不使用位图字体?您可以构建一个带有透明通道的png图像。我认为SDL_ttf与相同的系统一起工作,它内部使用位图字体构建图像。


1
请注意,@ForceMagic已删除了一个失效的链接。如果您能找到替代品,请将其添加回去。对于@ForceMagic,既然您要求删除失效的链接,那么请添加一条评论(这将通知发布者),以便他们可以收到通知并找到新的(或替代)链接。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接