0

I'm trying to blit a surface created by IMG_Load (SDL_image) onto one created via SDL_CreateRGBSurface. When both surfaces are loaded with IMG_Load, this works fine, but not when the target surface was created using SDL_CreateRGBSurface.

By experimentation, I figured out that if I call SDL_SetAlpha on the source surface, it suddenly works fine. The documentation is a bit lacking, but how I understand it, the way I call it below should clear the SDL_SRCALPHA flag, presumably set by IMG_Load. It seems like the alpha flag of the source surface is what matters when blitting, so I guess this turns off alpha blending altogether.

The big question is: Why is this SDL_SetAlpha necessary in the first place? And is this really the right way to do it?

Here's code that reproduces this:

#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\n");
        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;
}

You'll need source.png. I'm using a 400x400 transparent PNG with some black scribbling.

4

1 回答 1

1

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

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

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

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

  1. 为源表面禁用 SRCALPHA(您已经尝试过)
  2. 为目标表面禁用 SRCALPHA,因此它不会与屏幕混合。它不会影响初始 blit,但会影响下一个 blit,当您的“目标”(或“副本”)用作源以在屏幕上进行 blit 时。
  3. 用不透明的白色填充目标表面。但是,它确实会在源的 [部分] 透明部分上影响 blitting(alpha 与白色和黑色混合会产生不同的结果)。
  4. 手动复制像素。如果像素格式和表面尺寸匹配,memcpy那么就足够了。这将覆盖目的地的内容,而不是混合它。它也将明显快于混合。如果尺寸不匹配 - 根据pitch参数,您可能需要 number_of_rows memcpy。如果像素格式不匹配 - 复制有问题,您需要从源像素格式解码每个像素并将其编码为目标像素格式。
  5. 使用SDL_ConvertSurface(例如SDL_ConvertSurface(source, source->format, 0)。它将创建具有相同内容的新表面。我认为这种方式是首选。不要忘记稍后释放新表面。
  6. 如果您使用 SDL 1.2,请使用SDL_DisplayFormatAlpha 。这与 几乎相同SDL_ConvertSurface,但将表面转换为显示格式,而不是源表面的格式。它不再在 SDL2 中可用。

另请注意,(4)、(5) 和 (6) 是我认为可以让您保持源 alpha 的唯一方法。所有的 blitting 操作都会丢弃源 alpha,给目标留下自己的 alpha 值——因为 blitting 永远不会更新目标 alpha。

于 2014-05-08T05:54:57.997 回答