Trying to Understand Alpha

I’m not understanding something conceptually here.

I have a PNG, it contains has some pixels with semi-transparency, some
fully opaque, some fully transparent.

I use SDL_image’s IMG_load function to get that PNG into a SDL_Surface (surfA).

Now, if I blit that entire surface (or a portion of that surface)
directly to the screen surface, the alpha is properly blended into the
pixels that are already on the screen surface. I get the effect I
want.

However, if I create a new SDL_Surface (surfB) using
SDL_CreateRGBSurface(), and I blit the contents of surfA to surfB -
THEN I try to blit surfB to the screen, the alpha pixels are not
properly blended.

Now I’ve tried to create the surfB surface using surfA’s flags,
format, masks and I’ve tried to create surfB using the screen’s
properties - neither one work properly.

I’ve tried to use SDL_SetAlpha() with SDL_SRCALPHA on surfB and that
doesn’t work - if I set the alpha level to something like 128 then the
contents of surfB (including what I think should be the fully
transparent background of surfB) is partially blended with the
screen’s pixels.

I’ve tried to fill surfB with fully transparent pixels first before
blitting surfA to surfB, but that doesn’t work either:

SDL_FillRect(surfB, NULL, SDL_MapRGBA(surfB->format, 0, 0, 0, 0));
SDL_BlitSUrface(surfA, NULL, surfB, NULL);

I need some help understanding what I’m doing (and thinking) wrong.

Thanks,
Jeff

P.S. Is anyone aware of any free code out there that will save a
SDL_Surface as a PNG (and preserve the alpha)? I’d like to use SVG to
batch combine, rotate, and cut a bunch of PNGs into a single PNG.

Ok, I got it now.

From http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fBlitSurface :

“note that when you’re blitting between two alpha surfaces, normally
the alpha of the destination acts as a mask. If you want to just do a
"dumb copy” that doesn’t blend, you have to turn off the SDL_SRCALPHA
flag on the source surface"

A lightbulb moment!

“…This is how it’s supposed to work, but can be surprising when
you’re trying to combine one image with another and both have
transparent backgrounds.”

Yes, it can be surprising! :slight_smile:

Regards,
JeffOn 7/20/07, Jeff Schiller <@Jeff_Schiller> wrote:

I’m not understanding something conceptually here.

I have a PNG, it contains has some pixels with semi-transparency, some
fully opaque, some fully transparent.

I use SDL_image’s IMG_load function to get that PNG into a SDL_Surface (surfA).

Now, if I blit that entire surface (or a portion of that surface)
directly to the screen surface, the alpha is properly blended into the
pixels that are already on the screen surface. I get the effect I
want.

However, if I create a new SDL_Surface (surfB) using
SDL_CreateRGBSurface(), and I blit the contents of surfA to surfB -
THEN I try to blit surfB to the screen, the alpha pixels are not
properly blended.

Now I’ve tried to create the surfB surface using surfA’s flags,
format, masks and I’ve tried to create surfB using the screen’s
properties - neither one work properly.

I’ve tried to use SDL_SetAlpha() with SDL_SRCALPHA on surfB and that
doesn’t work - if I set the alpha level to something like 128 then the
contents of surfB (including what I think should be the fully
transparent background of surfB) is partially blended with the
screen’s pixels.

I’ve tried to fill surfB with fully transparent pixels first before
blitting surfA to surfB, but that doesn’t work either:

SDL_FillRect(surfB, NULL, SDL_MapRGBA(surfB->format, 0, 0, 0, 0));
SDL_BlitSUrface(surfA, NULL, surfB, NULL);

I need some help understanding what I’m doing (and thinking) wrong.

Thanks,
Jeff

P.S. Is anyone aware of any free code out there that will save a
SDL_Surface as a PNG (and preserve the alpha)? I’d like to use SVG to
batch combine, rotate, and cut a bunch of PNGs into a single PNG.

Firstly here is what i use to copy png to a new surface, as the Alpha
prevents the blits.

void BlatRectFlat(SDL_Surface * Src,SDL_Rect *SrcRect,SDL_Rect *DestRect,
SDL_Surface * Dest)
{
Uint8 Sbpp = Src->format->BytesPerPixel;
Uint8 Dbpp = Dest->format->BytesPerPixel;
Uint8 *Sbits;
Uint8 *Dbits;
int x,y;
for(y=0;yh;y++)
{
for(x=0;xw;x++)
{
Sbits = ((Uint8
*)Src->pixels)+(((SrcRect->y+y)*Src->pitch)+((SrcRect->x+x)*Sbpp));
Dbits = ((Uint8
*)Dest->pixels)+(((DestRect->y+y)*Dest->pitch)+((DestRect->x+x)*Dbpp));
*((Uint32 *)(Dbits)) = *((Uint32 *)(Sbits));
}
}
}

Also the save png code simply call, int SavePng(SDL_Surface *surface, char
*FileName); sorry for the long listing on the SAVE PNG but i remember, it
took me a while to find this and get it working. You will need png,h from
the png source and obviously zlib, libpng DLLs on a windows system. But
after some creating or getting of these you should be able to save pngs
easily. One thing you must exit your program before you can look at the png,
as for some reason that I can’t be bothered to look for the outputted PNG is
not correctly formed until you exit your program.

Anyhow Enjoy

Trish x

/*

  • png_save.c*
  • You know, I can’t remember where I got this code. I’m pretty sure
  • it was freely posted on a mailing list.
  • If you are the author, and object to its redistribution, let me know.

*/

#include “SDL.h”
#include “SDL_image.h”
#include “png.h”
#include <stdlib.h>

/* Save a PNG type image to an SDL datasource */
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{
SDL_RWops *src;

src = (SDL_RWops *)png_get_io_ptr(ctx);
SDL_RWwrite(src, area, (int)size, 1);
}

static void png_io_flush(png_structp ctx)
{
SDL_RWops *src;

src = (SDL_RWops )png_get_io_ptr(ctx);
/
how do I flush src? */
}

static int png_colortype_from_surface(SDL_Surface surface)
{
int colortype = PNG_COLOR_MASK_COLOR; /
grayscale not supported */

if (surface->format->palette)
colortype |= PNG_COLOR_MASK_PALETTE;
else if (surface->format->Amask)
colortype |= PNG_COLOR_MASK_ALPHA;

return colortype;
}

static void png_user_warn(png_structp ctx, png_const_charp str)
{
fprintf(stderr, “libpng: warning: %s\n”, str);
}

static void png_user_error(png_structp ctx, png_const_charp str)
{
fprintf(stderr, “libpng: error: %s\n”, str);
}

int SavePng(SDL_Surface *surface, char *FileName)
{
SDL_RWops * src = SDL_RWFromFile(FileName, “w”);
SDL_LockSurface(surface);
png_structp png_ptr = 0;
png_infop info_ptr = 0;
png_colorp palette = 0;
png_bytep *row_pointers = 0;
int i;
int colortype;
int result = -1;

png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
png_user_error, png_user_warn);

if (png_ptr == NULL)
{
IMG_SetError(“Couldn’t allocate memory for PNG file”);
return -1;
}

/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
IMG_SetError(“Couldn’t create image information for PNG file”);
goto done;
}

/* Set error handling. /
if (setjmp(png_ptr->jmpbuf))
{
/
If we get here, we had a problem reading the file */
IMG_SetError(“Error writing the PNG file”);
goto done;
}

png_set_write_fn(png_ptr, src, png_write_data, png_io_flush);

/* Set the image information here. Width and height are up to 2^31,

  • bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
  • the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
  • PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
  • or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
  • PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
  • currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE.
    REQUIRED
    */
    colortype = png_colortype_from_surface(surface);
    png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8,
    colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
    PNG_FILTER_TYPE_BASE);

/* set the palette if there is one. REQUIRED for indexed-color images */
if (colortype & PNG_COLOR_MASK_PALETTE)
{
palette = (png_colorp)malloc(surface->format->palette->ncolors *
sizeof (png_color));

  if (!palette)

{
IMG_SetError(“Couldn’t allocate memory for PNG palette”);
goto done;
}

  for (i = 0; i < surface->format->palette->ncolors; i++)

{
palette[i].red = surface->format->palette->colors[i].r;
palette[i].green = surface->format->palette->colors[i].g;
palette[i].blue = surface->format->palette->colors[i].b;
}

  png_set_PLTE(png_ptr, info_ptr, palette,

surface->format->palette->ncolors);
}

/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);

/* pack pixels into bytes */
png_set_packing(png_ptr);

/* Create the array of pointers to image data /
row_pointers = (png_bytep
) malloc(sizeof(png_bytep)*surface->h);

if ( (row_pointers == NULL) )
{
IMG_SetError(“Couldn’t allocate PNG row pointers”);
goto done;
}

for (i = 0; i < surface->h; i++)
row_pointers[i] = (png_bytep)(Uint8 )surface->pixels +
i
surface->pitch;

/* write out the entire image data in one call /
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
result = 0; /
success! */

done:
if (row_pointers)
free(row_pointers);

if (info_ptr->palette)
free(info_ptr->palette);

png_destroy_write_struct(&png_ptr, (png_infopp)NULL);

SDL_UnlockSurface(surface);
return result;
}