Can't save .bmp in RGBA format

Started by
18 comments, last by programering 15 years, 4 months ago
I'm writing custom Save/LoadBMP functions to save/load SDL_Surface to/from .bmp in RGBA format, which I need for antialiasing sprite rotation later on. Right now I'm working on the save function but I don't get it to work. First I tried to write the BITMAPHEADER***(Both with BITMAPV4HEADER(Specially for the RGBA format) and BITMAPINFOHEADER) structs as saving method which didn't work, probably because it stored integer members in big-endian which the valid ones are in little-endian. Then I modify the SDL_SaveBMP from it's source(Copying it ofcoarse). First I tried with the BITMAPINFOHEADER info header format as it was orginally but it didn't work all the way, it only got to display its dimension but you didn't see the image. And so I tried with the V4 info header(BITMAPV4HEADER) and same fault. I'm out of ideas what the problem is to make this work so I wonder if you know why it doesn't get displayed. Here's that latest code version:

bool SaveRGBA32BMP_RW(SDL_Surface *surf, SDL_RWops *dst, bool freedst)
{
	bool success = true;
	long fp_offset;
	Uint32 *bits;

	/* The Win32 BMP file header (14 bytes) */
	char   magic[2] = {'B','M'};
	Uint32 bfSize;
	Uint16 bfReserved1;
	Uint16 bfReserved2;
	Uint32 bfOffBits;

	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
	Uint32 biSize;
	Sint32 biWidth;
	Sint32 biHeight;
	Uint16 biPlanes;
	Uint16 biBitCount;
	Uint32 biCompression;
	Uint32 biSizeImage;
	Sint32 biXPelsPerMeter;
	Sint32 biYPelsPerMeter;
	Uint32 biClrUsed;
	Uint32 biClrImportant;
	Uint32 bV4RedMask;
	Uint32 bV4GreenMask;
	Uint32 bV4BlueMask;
	Uint32 bV4AlphaMask;
	Uint32 bV4CSType;
//	CIEXYZTRIPLE  bV4Endpoints;
	long   bV4Endpoints;
	Uint32 bV4GammaRed;
	Uint32 bV4GammaGreen;
	Uint32 bV4GammaBlue;

	/* Make sure we have somewhere to save */
	if (surf && dst)
	{
		if (surf->format->BitsPerPixel != 32)
		{
			success = false;
			goto done;
		}

		const int bw = surf->w * surf->format->BytesPerPixel;

		/* Set the BMP file header values */
		bfSize = 0;		 /* We'll write this when we're done */
		bfReserved1 = 0;
		bfReserved2 = 0;
		bfOffBits = 0;		/* We'll write this when we're done */

		/* Write the BMP file header values */
		fp_offset = SDL_RWtell(dst);
		SDL_ClearError();
		SDL_RWwrite(dst, magic, 2, 1);
		SDL_WriteLE32(dst, bfSize);
		SDL_WriteLE16(dst, bfReserved1);
		SDL_WriteLE16(dst, bfReserved2);
		SDL_WriteLE32(dst, bfOffBits);

		/* Set the BMP info values */
		biSize = 40;	//180;	// 40;
		biWidth = surf->w;
		biHeight = surf->h;
		biPlanes = 1;
		biBitCount = surf->format->BitsPerPixel;
		biCompression = BI_RGB;
		biSizeImage = surf->h * surf->pitch;
		biXPelsPerMeter = 0;
		biYPelsPerMeter = 0;
		biClrUsed = 0;
		biClrImportant = 0;
		bV4RedMask   = /*surf->format->*/Rmask;
		bV4GreenMask = /*surf->format->*/Gmask;
		bV4BlueMask  = /*surf->format->*/Bmask;
		bV4AlphaMask = /*surf->format->*/Amask;
		bV4CSType = 0;
//	CIEXYZTRIPLE  bV4Endpoints;
		bV4Endpoints = 0;
		bV4GammaRed = 0;
		bV4GammaGreen = 0;
		bV4GammaBlue = 0;

		/* Write the BMP info values */
		SDL_WriteLE32(dst, biSize);
		SDL_WriteLE32(dst, biWidth);
		SDL_WriteLE32(dst, biHeight);
		SDL_WriteLE16(dst, biPlanes);
		SDL_WriteLE16(dst, biBitCount);
		SDL_WriteLE32(dst, biCompression);
		SDL_WriteLE32(dst, biSizeImage);
		SDL_WriteLE32(dst, biXPelsPerMeter);
		SDL_WriteLE32(dst, biYPelsPerMeter);
		SDL_WriteLE32(dst, biClrUsed);
		SDL_WriteLE32(dst, biClrImportant);

		SDL_WriteLE32(dst,bV4RedMask);
		SDL_WriteLE32(dst,bV4GreenMask);
		SDL_WriteLE32(dst,bV4BlueMask);
		SDL_WriteLE32(dst,bV4AlphaMask);
		SDL_WriteLE32(dst,bV4CSType);
		for (int i = 0; i < 9; i++)
		{
		//	SDL_RWwrite(dst,&bV4Endpoints,4,1);
			SDL_WriteLE32(dst,0);
		}
		SDL_WriteLE32(dst,bV4GammaRed);
		SDL_WriteLE32(dst,bV4GammaGreen);
		SDL_WriteLE32(dst,bV4GammaBlue);

		/* Write the bitmap offset */
		bfOffBits = SDL_RWtell(dst)-fp_offset;
		if ( SDL_RWseek(dst, fp_offset+10, RW_SEEK_SET) < 0 ) {
			SDL_Error(SDL_EFSEEK);
		}
		SDL_WriteLE32(dst, bfOffBits);
		if ( SDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
			SDL_Error(SDL_EFSEEK);
		}

		/* Write the bitmap image upside down */
		bits = (Uint32 *)surf->pixels + (surf->h * surf->pitch);
		while (bits > (Uint32 *)surf->pixels)
		{
			bits -= surf->pitch;

			if (SDL_RWwrite(dst, bits, bw, 1) < 0)
			{
				SDL_Error(SDL_EFWRITE);
				break;
			}
		}

		// Get the file size:
		bfSize = SDL_RWtell(dst)-fp_offset;
		// Set the file pointer back to:
		if (SDL_RWseek(dst, fp_offset+2, RW_SEEK_SET) < 0)
		{
			SDL_Error(SDL_EFSEEK);
		}
		// Write the file size:
		SDL_WriteLE32(dst, bfSize);
		// Set it back to the end once again:
		if (SDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0)
		{
			SDL_Error(SDL_EFSEEK);
		}

	}

done:
	if (freedst && dst)
	{
		SDL_RWclose(dst);
	}

	return success;
}



Here's the Palette/RGB to RGBA converting function BTW:

SDL_Surface *ConvRGBA32(SDL_Surface *orig)
{
	int width = orig->w;
	int height = orig->h;
	SDL_Surface *rgba = SDL_CreateRGBSurface(SDL_SWSURFACE,width,height,32,Rmask,Gmask,Bmask,Amask);
	Uint32 npixels = width * height;
	SDL_PixelFormat *format = orig->format;
	SDL_Palette *palette = format->palette;
	SDL_Color *entry;
	Uint8 bpp = format->BytesPerPixel;
	Uint32 *dstpix = (Uint32 *)rgba->pixels;
	Uint8 *srcpix = (Uint8 *)orig->pixels;
	Uint8 r, g, b, a;
	Uint32 color;

	for (Uint32 i = 0; i < npixels; i++)
	{
		if (format->BitsPerPixel == 8 && palette)
		{
			entry = palette->colors + *srcpix;
			r = entry->r;
			g = entry->g;
			b = entry->b;
			color = SDL_MapRGB(format,r,g,b);
		}
		else
		{
			color = *(Uint32 *)srcpix;
			SDL_GetRGB(color,format,&r,&g,&b);
		}

		if (color == format->colorkey)
			a = 0x00;
		else
			a = 0xff;

		*dstpix++ = SDL_MapRGBA(rgba->format,r,g,b,a);
		srcpix += bpp;
	}

	return rgba;
}



Thanks for your help.
Advertisement
BMP files don't contain alpha - they only hold RGB values. In a 32-bit BMP file, the 4th byte of every 4-byte pixel is "reserved" and is just used for padding.

Also, define "doesn't work". Does the image not load up in an image editor? Can you not load the image again using SDL?
Since you're using SDL you could use the SDL_image library instead, and a format like TGA if you need an alpha channel. That would be alot easier compared to writing your own format.
Quote:Original post by Evil Steve
BMP files don't contain alpha - they only hold RGB values. In a 32-bit BMP file, the 4th byte of every 4-byte pixel is "reserved" and is just used for padding.

That's why I used the BITMAPV4HEADER, I thought it supported RGBA, it has the RGBA masks specification though.

Quote:
Also, define "doesn't work". Does the image not load up in an image editor? Can you not load the image again using SDL?

I haven't tried to see if it works to load it back on, it surely will cos I use my own special-designed function. But the idea was to have ordinary valid .bmp format images but in RGBA format cos I need it.

Quote:Original post by Perost
Since you're using SDL you could use the SDL_image library instead, and a format like TGA if you need an alpha channel. That would be alot easier compared to writing your own format.

SDL_image has no save surface functions only for loading them.

Quote:Original post by programering
That's why I used the BITMAPV4HEADER, I thought it supported RGBA, it has the RGBA masks specification though.


Yes it has the specification, but no it is not necessarily what you think it is.

The 32-bit formats specify: "Each DWORD in the bitmap array represents the relative intensities of blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used."

Many programs will write your alpha channel in them, but they can correctly ignore those values.



The alpha masks are still used and useful, however.

When you are creating an icon or cursor (which support transparency) you can assign an alpha mask to it. See MSDN article on alpha blended cursors and icons.
There are many image formats that are designed to store RGBA: TGA, PNG, TIFF and DDS to name a few. There are also many excellent libraries that support reading and writing said formats, so I can't see a need to shoehorn BMP files into storing data they were not designed to contain.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Yes, you can write out 32-bit bitmaps. And you don't have to do anything special or use any special headers.
Just use the old format and set biBitCount to 32 instead of 24. Photoshop loads up 32-bitmaps with no problems.
Quote:Original post by samster581
Yes, you can write out 32-bit bitmaps. And you don't have to do anything special or use any special headers.
Just use the old format and set biBitCount to 32 instead of 24. Photoshop loads up 32-bitmaps with no problems.
Not with alpha though, as far as I know.
Quote:Original post by Evil Steve
Not with alpha though, as far as I know.


Yes, with alpha. I've done it before.
And Photoshop (CS2) lets you export 32-bit bitmaps too.

Just try it if you don't believe me.


This topic is closed to new replies.

Advertisement