关于图形:SDL:如何正确地将一个表面涂抹到使用SDL_CreateRGBSurface创建的表面上?

SDL: How to blit a surface onto one created with SDL_CreateRGBSurface correctly?

我正在尝试将IMG_Load(SDL_image)创建的表面涂抹到通过SDL_CreateRGBSurface创建的表面上。 当两个表面都加载了IMG_Load时,此方法可以正常工作,但是当使用SDL_CreateRGBSurface创建目标表面时则无法正常工作。

通过实验,我发现如果在源代码表面调用SDL_SetAlpha,它突然可以正常工作。 该文档有些缺乏,但是据我的理解,我在下面的称呼方式应该清除SDL_SRCALPHA标志,该标志可能是由IMG_Load设置的。 看起来像源表面的Alpha标志在发条时很重要,所以我猜想这会完全关闭Alpha混合。

最大的问题是:为什么首先需要SDL_SetAlpha? 这真的是正确的方法吗?

这是复制此代码的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <SDL/SDL.h>
#include <SDL_image/SDL_image.h>

SDL_Surface* copy_surface(SDL_Surface* source)
{
    SDL_Surface *target;

    target = SDL_CreateRGBSurface(0, source->w, source->h,
                                  source->format->BitsPerPixel,
                                  source->format->Rmask, source->format->Gmask,
                                  source->format->Bmask, source->format->Amask);

    /*
     * I really don't understand why this is necessary. This is supposed to
     * clear the SDL_SRCALPHA flag presumably set by IMG_Load. But why wouldn't
     * blitting work otherwise?
     */
    SDL_SetAlpha(source, 0, 0);

    SDL_BlitSurface(source, 0, target, 0);
    return target;
}

int main(int argc, char* argv[])
{
    SDL_Event event;
    SDL_Surface *copy, *screen, *source;

    SDL_Init(SDL_INIT_VIDEO);
    screen = SDL_SetVideoMode(800, 600, 0, 0);
    SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));
    source = IMG_Load("source.png");
    if (!source) {
        fprintf(stderr,"Unable to load source image\
");
        return 1;
    }
    copy = copy_surface(source);
    SDL_BlitSurface(copy, 0, screen, 0);
    SDL_Flip(screen);
    while (SDL_WaitEvent(&event))
        if (event.type == SDL_QUIT
            || (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
            break;
    SDL_Quit();
    return 0;
}

您将需要source.png。 我正在使用带有一些黑色斑点的400x400透明PNG。


http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsetalpha.html描述了每种可能的像素格式组合的划线。

执行alpha混合时,标准公式为SRC_COLOUR * SRC_ALPHA + DST_COLOUR * (1 - SRC_ALPHA)。在此未使用目标字母。

如果启用了Alpha混合,并且对源表面启用了SRCALPHA标志,则使用源Alpha重新计算(混合)更新的目标颜色,但保持目标Alpha通道不变。下次将此" dst"用作发短信的来源时,将使用其alpha。

为避免您的问题,有几种可能的方法:

  • 对源表面禁用SRCALPHA(您已经尝试过了)
  • 对目标表面禁用SRCALPHA,因此不会与屏幕混合。当您的"目标"(或"副本")用作屏幕上的blit的来源时,它不会影响初始blit,但会影响下一个blit。
  • 用不透明的白色填充目标表面。但是,它确实会影响源的[部分]透明部分上的斑点(与白色和黑色进行alpha混合会产生不同的结果)。
  • 手动复制像素。如果像素格式和表面尺寸匹配,则仅memcpy就足够了。这将覆盖目标的内容,而不是将其混合。它也比混合快得多。如果尺寸不匹配-根据pitch参数,您可能需要number_of_rows memcpy个。如果像素格式不匹配-复制存在问题,则需要从源像素格式解码每个像素并将其编码为目标像素格式。
  • 使用SDL_ConvertSurface(例如SDL_ConvertSurface(source, source->format, 0)。它将创建具有相同内容的新表面。我认为这种方法是首选。不要忘记稍后释放新表面。
  • 如果使用SDL 1.2,请使用SDL_DisplayFormatAlpha。这与SDL_ConvertSurface几乎相同,但是将表面转换为显示格式,而不是源表面的格式。它在SDL2中不再可用。
  • 另请注意,我看到的(4),(5)和(6)只是允许您保留源alpha的方式。所有的blitting操作都将丢弃源alpha,而为目标保留其自己的alpha值-因为blitting从不更新目标alpha。