Bug Report on SDL 1.1.8, was Re: Transparency not that transparent

Hi all,

Okay, I’ve tracked down the offending statement. This appears
to be an SDL bug, since it doesn’t seem to agree with the
descriptions I’ve received on this list.

At the bottom of the message you will find a snippet of code
with a blow-by-blow description of what happens, but basically
it comes down to this:

SUMMARY:
SDL_BlitSurface() does not do a correct alpha blit from an RGBA
alpha surface to an RGBA alpha surface. Instead, all RGB
data are blitted correctly, but the alpha data is lost. The
destination image is left with alpha = 0x00 for all pixels,
regardless of the alpha channel information in the source
image.

This occurs for the specific case of two SDL_SWSURFACE 32-bit
RGBA surfaces. I have not verified all other cases. However,
blitting the source to the screen surface (i.e. the one returned
by SDL_SetVideoMode() appears to work correctly (thank goodness,
otherwise debugging would’ve been really hard!).

If one first converts the destination surface to the screen
format, using SDL_DisplayFormat(), the problem is evaded and
the program works as expected (This is my recommended work-around,
and I’ll probably use this to fix my program).

Turning off SDL_SRCALPHA by using SDL_SetAlpha() “fixes” the
blit in a somewhat trivial way (since the alpha channel will
just be thrown away). Interestingly, you can’t do this by
simply omitting the SDL_SRCALPHA flag, you have to force it
down if you provided a mask for the alpha channel.

Thank you,
Terry Hancock

Mattias Engdeg?rd wrote:

  1. An 32-bit RGBA surface (with SDL_SRCALPHA – possibly the
    result from the above operation) is blitted onto another
    32-bit RGBA surface which may be blank/transparent, or may
    have previous RGBA data in it.

In the transparent case, the result should be essentially
identical to the source RGBA image. In the case where
there was previously data, the previous (dest) data would
show through wherever the source image is transparent
(in my particular example, there’s no partial transparency,
but there could be in principle).

since you are using SRCALPHA, the operation is to alpha-blend the
source onto the destination, as if the destination was opaque
(destination alpha channel is not used in the process and is kept
unchanged).

if you want to just copy the contents, alpha and all, to another
RGBA surface, make sure SDL_SRCALPHA is not set on the source
surface

LIBRARIES:
SDL 1.1.8
SDL Image 1.1.0
SGE 1.2.24 (this doesn’t appear to involve SGE code, though)

CODE SNIPPET:
Here’s the relevant piece of source code. The problem occurs
in the call marked with “***********” and “PROBLEM HERE” :

//
// Excerpted example code with results:

SDL_Surface *screen;
SDL_Surface *org, *cel, *layer, *layup;

  // Naturally we had to declare all the code --
  // initial and final code are omitted here

org = IMG_Load(“Background/arbor.set1.c1.fount.gif”);
seeSDL_info(org, “original”);
seeSDL_transparency(org);

  // We view the information & transparency on this image as
  // loaded by SDL Image library.
  //
  // 8-Bit Palette image.  Color keyed.  No alpha channel.

cel = SDL_CreateRGBSurface( SDL_SWSURFACE|SDL_SRCALPHA, 800, 600,
32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);

  // Fresh surface: 32-bit RGBA alpha surface
  // black + transparent everywhere.

istat = -1000;
istat = SDL_BlitSurface(org, NULL, cel, NULL);
printf(“org->cel blit returns: %d\n”, istat);

  // Now we blit the color keyed original onto the alpha
  // surface.  This works.

seeSDL_info(cel, “cel”); /* Check cel is okay first. */
seeSDL_transparency(cel);

  // "cel" is RGBA alpha surface.
  // Contains RGB picture data from original.
  // alpha channel is converted
  // from original color-key, so where the colorkey color
  // was in the original is filled with 0x00000000, i.e.
  // black + fully transparent.

layer = SDL_CreateRGBSurface( SDL_SWSURFACE|SDL_SRCALPHA, 800, 600,
32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);

  // Another fresh RGBA 32-bit alpha surface

//*****************************************************************
istat = -1000;
istat = SDL_BlitSurface(cel, NULL, layer, NULL); // PROBLEM HERE
printf(“cel->layer blit returns: %d\n”, istat);
//*****************************************************************

  // This should do an alpha blit of the cel RGBA surface
  // onto the layer RGBA surface.
  // This DOES NOT WORK, as we shall see.

//sge_rotate_scaled(layer, cel, 600, 320, 30, 1.5);
// This alternative, which works pixel-by-pixel, but doesn’t
// support alpha blending, works as expected.

seeSDL_info(layer, “layer”);
seeSDL_transparency(layer);

  // Now we see the problem, the resulting image is transparent
  // everywhere (alpha channel is 0x00 for all pixels), despite
  // the blit operation, which should have carried opaque
  // pixels from the cel, resulting in opaque pixels here.

SDL_SetColorKey(org, 0, 0x00);
SDL_BlitSurface(org, NULL, screen, NULL);
sge_UpdateRect(screen, 0,0,0,0);

  // This shows the original image, with the color key shut
  // off to produce an opaque blit. Because the colorkey was
  // visible (a light tan color), it provides a background for
  // the next blit.

//SDL_SetAlpha(layer, 0, 0xFF); /* Force opaque display */

SDL_SetAlpha(layer, 0, 0xFF); /* Force opaque display */
SDL_BlitSurface(layer, NULL, screen, NULL);
sge_UpdateRect(screen, 0,0,0,0);

  // As is, this blit does nothing -- this is the correct behavior
  // for a surface with transparent alpha in all pixels, so it
  // simply shows the error in the cel->layer blit.
  //
  // However, we can verify that the RGB data was blitted by
  // turning off the SDL_SRCALPHA flag for this surface before
  // displaying it (note the commented-out SetAlpha statement).

OUTPUT:
The seeSDL_info() functions spits out some info about flag states
and surface properties, the calls listed above produce the following
output:

Surface " original"
800 x 606 x 8 [-][-][KEY][—][-----]
Color Key = ( E6 DF C5 ) Per-Surface Alpha = FF
Masks: R=00000000 G=00000000 B=00000000 A=00000000
Number of Colors in Palette: 256-----------------------

org->cel blit returns: 0
Surface " cel"
800 x 600 x 32 [-][-][—][—][ALPHA]
Color Key = ( 00 00 00 ) Per-Surface Alpha = FF
Masks: R=FF000000 G=00FF0000 B=0000FF00 A=000000FF

cel->layer blit returns: 0
Surface " layer"
800 x 600 x 32 [-][-][—][—][ALPHA]
Color Key = ( 00 00 00 ) Per-Surface Alpha = FF
Masks: R=FF000000 G=00FF0000 B=0000FF00 A=000000FF


Terry Hancock
@Terry_Hancock

At the bottom of the message you will find a snippet of code
with a blow-by-blow description of what happens, but basically
it comes down to this:

please don’t post snippets, but a complete minimal program that compiles,
without dependencies on any other libs (like SDL_image or SGE), unless
you suspect bugs in SDL_image (I won’t debug SGE for you). the shorter
you make it the faster we can spot the bugs (preferably <20 lines)

SDL_BlitSurface() does not do a correct alpha blit from an RGBA
alpha surface to an RGBA alpha surface. Instead, all RGB
data are blitted correctly, but the alpha data is lost. The
destination image is left with alpha = 0x00 for all pixels,
regardless of the alpha channel information in the source
image.

RGBA->RGBA blits (with SDL_SRCALPHA set) alpha-blend the source RGBA
onto the destination RGB, and the result retains the unmodified alpha
of the destination. see my earlier posts for details, and the
documentation for SDL_SetAlpha

SDL does not currently implement proper RGBA->RGBA compositioning since
doing so without premultiplied alpha cannot be done in an efficient way.
I’d like to have it in the next major release, but for now you have to
cope with it or work around it in some way

Turning off SDL_SRCALPHA by using SDL_SetAlpha() “fixes” the
blit in a somewhat trivial way (since the alpha channel will
just be thrown away). Interestingly, you can’t do this by
simply omitting the SDL_SRCALPHA flag, you have to force it
down if you provided a mask for the alpha channel.

yes it’s a misfeature that SDL_CreateRGBSurface() sets SDL_SRCALPHA
automatically if you specified an alpha channel. it should be documented

Mattias Engdeg?rd wrote:

At the bottom of the message you will find a snippet of code
with a blow-by-blow description of what happens, but basically
it comes down to this:

please don’t post snippets, but a complete minimal program that compiles,
without dependencies on any other libs (like SDL_image or SGE), unless
you suspect bugs in SDL_image (I won’t debug SGE for you). the shorter
you make it the faster we can spot the bugs (preferably <20 lines)

Noted. However in this case, the debugging code that makes the
error visible depends on SGE and you need SDL_Image to get a
sample resource to work with. It would have been very difficult
to demonstrate the problem without those.

RGBA->RGBA blits (with SDL_SRCALPHA set) alpha-blend the source RGBA
onto the destination RGB, and the result retains the unmodified alpha
of the destination. see my earlier posts for details, and the
documentation for SDL_SetAlpha

SDL does not currently implement proper RGBA->RGBA compositioning since
doing so without premultiplied alpha cannot be done in an efficient way.
I’d like to have it in the next major release, but for now you have to
cope with it or work around it in some way

Okay. As I said, I already worked a way around it – but it
wasn’t what I expected and I didn’t see it in the documentation,
which might be because I’m looking in the wrong places or
not reading it carefully enough. No offense was intended –
I just thought you felt this was the "conceptually correct"
behavior for the blit and wanted to make the case for the
alternative.

It turns out that I needed the workaround anyway, since
I really need to do some transformations on the data in the
process (my transformation code handles the alpha channel
by essentially blitting the RGBA channels “opaquely” onto the
new surface – i.e. it just moves pixels around, which is
what the sge_rotate…() functions do as well). Using a
straight blit was really just an intermediate step.

Turning off SDL_SRCALPHA by using SDL_SetAlpha() “fixes” the
blit in a somewhat trivial way (since the alpha channel will
just be thrown away). Interestingly, you can’t do this by
simply omitting the SDL_SRCALPHA flag, you have to force it
down if you provided a mask for the alpha channel.

yes it’s a misfeature that SDL_CreateRGBSurface() sets SDL_SRCALPHA
automatically if you specified an alpha channel. it should be documented

This might actually be a desired behavior – certainly it doesn’t
make much sense to ask for an alpha mask if you don’t want it to
be used. If I wasn’t fiddling with the flags so much in investigating
this I would probably never have noticed.–
Terry Hancock
@Terry_Hancock

Noted. However in this case, the debugging code that makes the
error visible depends on SGE and you need SDL_Image to get a
sample resource to work with. It would have been very difficult
to demonstrate the problem without those.

not really — use SDL_FillRect() to fill areas with various colours
(and transparency, if applicable). Or just set some pixels manually

I just thought you felt this was the "conceptually correct"
behavior for the blit and wanted to make the case for the
alternative.

it’s a known problem but according to Sam the current semantics
(keep destination alpha) is useful for the OPENGLBLIT mode so we
did it that way