Skip to content

Commit

Permalink
Fixed bug 3352 - Adding alpha mask support to SDL_SaveBMP_RW
Browse files Browse the repository at this point in the history
Simon Hug

The current SDL_SaveBMP_RW function that saves surfaces to a BMP uses an old bitmap header which doesn't officially support alpha channels. Applications just ignore the byte where the alpha is stored. This can easily be extended by using a newer header version and setting the alpha mask.

The attached patch has these changes:

- Extending the description of the function in the SDL_surface.h header with the supported formats.
- Refining when surfaces get stored to a 32-bit BMP. (Must have bit depth of 8 or higher and must have an alpha mask or colorkey.)
- Fixing a small bug that saves 24-bit BGR surfaces with a colorkey in a 24-bit BMP.
- Adding code that switches to the bitmap header version 4 if the surface has an alpha mask or colorkey. (I chose version 4 because Microsoft didn't lose its documentation behind a file cabinet like they did with version 3.)
- Adding a hint that can disable the use of the version 4 header. This is for people that need the legacy header or like the old behavior better. (I'm not sure about the hint name, though. May need changing if there are any rules to that.)
  • Loading branch information
slouken committed Oct 1, 2016
1 parent 53e22e4 commit 9fff05f
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 4 deletions.
19 changes: 19 additions & 0 deletions include/SDL_hints.h
Expand Up @@ -669,6 +669,25 @@ extern "C" {
*/
#define SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"

/**
* \brief Prevent SDL from using version 4 of the bitmap header when saving BMPs.
*
* The bitmap header version 4 is required for proper alpha channel support and
* SDL will use it when required. Should this not be desired, this hint can
* force the use of the 40 byte header version which is supported everywhere.
*
* The variable can be set to the following values:
* "0" - Surfaces with a colorkey or an alpha channel are saved to a
* 32-bit BMP file with an alpha mask. SDL will use the bitmap
* header version 4 and set the alpha mask accordingly.
* "1" - Surfaces with a colorkey or an alpha channel are saved to a
* 32-bit BMP file without an alpha mask. The alpha channel data
* will be in the file, but applications are going to ignore it.
*
* The default value is "0".
*/
#define SDL_HINT_BMP_SAVE_LEGACY_FORMAT "SDL_BMP_SAVE_LEGACY_FORMAT"

/**
* \brief An enumeration of hint priorities
*/
Expand Down
6 changes: 6 additions & 0 deletions include/SDL_surface.h
Expand Up @@ -184,6 +184,12 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_LoadBMP_RW(SDL_RWops * src,
/**
* Save a surface to a seekable SDL data stream (memory or file).
*
* Surfaces with a 24-bit, 32-bit and paletted 8-bit format get saved in the
* BMP directly. Other RGB formats with 8-bit or higher get converted to a
* 24-bit surface or, if they have an alpha mask or a colorkey, to a 32-bit
* surface before they are saved. YUV and paletted 1-bit and 4-bit formats are
* not supported.
*
* If \c freedst is non-zero, the stream will be closed after being written.
*
* \return 0 if successful or -1 if there was an error.
Expand Down
63 changes: 59 additions & 4 deletions src/video/SDL_bmp.c
Expand Up @@ -32,6 +32,7 @@
This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
*/

#include "SDL_hints.h"
#include "SDL_video.h"
#include "SDL_assert.h"
#include "SDL_endian.h"
Expand All @@ -47,6 +48,11 @@
#define BI_BITFIELDS 3
#endif

/* Logical color space values for BMP files */
#ifndef LCS_WINDOWS_COLOR_SPACE
/* 0x57696E20 == "Win " */
#define LCS_WINDOWS_COLOR_SPACE 0x57696E20
#endif

static void CorrectAlphaChannel(SDL_Surface *surface)
{
Expand Down Expand Up @@ -457,6 +463,8 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
int i, pad;
SDL_Surface *surface;
Uint8 *bits;
SDL_bool save32bit = SDL_FALSE;
SDL_bool saveLegacyBMP = SDL_FALSE;

/* The Win32 BMP file header (14 bytes) */
char magic[2] = { 'B', 'M' };
Expand All @@ -478,14 +486,24 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
Uint32 biClrUsed;
Uint32 biClrImportant;

/* The additional header members from the Win32 BITMAPV4HEADER struct (108 bytes in total) */
Uint32 bV4RedMask = 0;
Uint32 bV4GreenMask = 0;
Uint32 bV4BlueMask = 0;
Uint32 bV4AlphaMask = 0;
Uint32 bV4CSType = 0;
Sint32 bV4Endpoints[3 * 3] = {0};
Uint32 bV4GammaRed = 0;
Uint32 bV4GammaGreen = 0;
Uint32 bV4GammaBlue = 0;

/* Make sure we have somewhere to save */
surface = NULL;
if (dst) {
SDL_bool save32bit = SDL_FALSE;
#ifdef SAVE_32BIT_BMP
/* We can save alpha information in a 32-bit BMP */
if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
saveme->format->Amask) {
if (saveme->format->BitsPerPixel >= 8 && (saveme->format->Amask ||
saveme->map->info.flags & SDL_COPY_COLORKEY)) {
save32bit = SDL_TRUE;
}
#endif /* SAVE_32BIT_BMP */
Expand All @@ -497,7 +515,7 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
SDL_SetError("%d bpp BMP files not supported",
saveme->format->BitsPerPixel);
}
} else if ((saveme->format->BitsPerPixel == 24) &&
} else if ((saveme->format->BitsPerPixel == 24) && !save32bit &&
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
(saveme->format->Rmask == 0x00FF0000) &&
(saveme->format->Gmask == 0x0000FF00) &&
Expand Down Expand Up @@ -537,6 +555,13 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
return -1;
}

if (save32bit) {
const char *hint = SDL_GetHint(SDL_HINT_BMP_SAVE_LEGACY_FORMAT);
if (hint != NULL && (hint[0] == '1' && hint[1] == 0)) {
saveLegacyBMP = SDL_TRUE;
}
}

if (surface && (SDL_LockSurface(surface) == 0)) {
const int bw = surface->w * surface->format->BytesPerPixel;

Expand Down Expand Up @@ -572,6 +597,21 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
}
biClrImportant = 0;

/* Set the BMP info values for the version 4 header */
if (save32bit && !saveLegacyBMP) {
biSize = 108;
biCompression = BI_BITFIELDS;
/* The BMP format is always little endian, these masks stay the same */
bV4RedMask = 0x00ff0000;
bV4GreenMask = 0x0000ff00;
bV4BlueMask = 0x000000ff;
bV4AlphaMask = 0xff000000;
bV4CSType = LCS_WINDOWS_COLOR_SPACE;
bV4GammaRed = 0;
bV4GammaGreen = 0;
bV4GammaBlue = 0;
}

/* Write the BMP info values */
SDL_WriteLE32(dst, biSize);
SDL_WriteLE32(dst, biWidth);
Expand All @@ -585,6 +625,21 @@ SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
SDL_WriteLE32(dst, biClrUsed);
SDL_WriteLE32(dst, biClrImportant);

/* Write the BMP info values for the version 4 header */
if (save32bit && !saveLegacyBMP) {
SDL_WriteLE32(dst, bV4RedMask);
SDL_WriteLE32(dst, bV4GreenMask);
SDL_WriteLE32(dst, bV4BlueMask);
SDL_WriteLE32(dst, bV4AlphaMask);
SDL_WriteLE32(dst, bV4CSType);
for (i = 0; i < 3 * 3; i++) {
SDL_WriteLE32(dst, bV4Endpoints[i]);
}
SDL_WriteLE32(dst, bV4GammaRed);
SDL_WriteLE32(dst, bV4GammaGreen);
SDL_WriteLE32(dst, bV4GammaBlue);
}

/* Write the palette (in BGR color order) */
if (surface->format->palette) {
SDL_Color *colors;
Expand Down

0 comments on commit 9fff05f

Please sign in to comment.