Deal with various .bmp file format variants in SDL_LoadBMP_RW().

This helps when modern versions of The Gimp (and lots of other things)
produces a 32-bit bitmap with an alpha channel, or anything with "BI_BITFIELDS"
format, since that data is now embedded in the bitmap info header instead of
directly following it and we would accidentally skip over embedded versions of
it.

Fixes Bugzilla #2714.
main
Ryan C. Gordon 2014-09-08 01:36:22 -04:00
parent 0b98d0cb8a
commit bcaae0b577
1 changed files with 84 additions and 69 deletions

View File

@ -85,15 +85,17 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
int bmpPitch; int bmpPitch;
int i, pad; int i, pad;
SDL_Surface *surface; SDL_Surface *surface;
Uint32 Rmask; Uint32 Rmask = 0;
Uint32 Gmask; Uint32 Gmask = 0;
Uint32 Bmask; Uint32 Bmask = 0;
Uint32 Amask; Uint32 Amask = 0;
SDL_Palette *palette; SDL_Palette *palette;
Uint8 *bits; Uint8 *bits;
Uint8 *top, *end; Uint8 *top, *end;
SDL_bool topDown; SDL_bool topDown;
int ExpandBMP; int ExpandBMP;
SDL_bool haveRGBMasks = SDL_FALSE;
SDL_bool haveAlphaMask = SDL_FALSE;
SDL_bool correctAlpha = SDL_FALSE; SDL_bool correctAlpha = SDL_FALSE;
/* The Win32 BMP file header (14 bytes) */ /* The Win32 BMP file header (14 bytes) */
@ -144,15 +146,14 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
/* Read the Win32 BITMAPINFOHEADER */ /* Read the Win32 BITMAPINFOHEADER */
biSize = SDL_ReadLE32(src); biSize = SDL_ReadLE32(src);
if (biSize == 12) { if (biSize == 12) { /* really old BITMAPCOREHEADER */
biWidth = (Uint32) SDL_ReadLE16(src); biWidth = (Uint32) SDL_ReadLE16(src);
biHeight = (Uint32) SDL_ReadLE16(src); biHeight = (Uint32) SDL_ReadLE16(src);
/* biPlanes = */ SDL_ReadLE16(src); /* biPlanes = */ SDL_ReadLE16(src);
biBitCount = SDL_ReadLE16(src); biBitCount = SDL_ReadLE16(src);
biCompression = BI_RGB; biCompression = BI_RGB;
} else { } else if (biSize >= 40) { /* some version of BITMAPINFOHEADER */
const unsigned int headerSize = 40; Uint32 headerSize;
biWidth = SDL_ReadLE32(src); biWidth = SDL_ReadLE32(src);
biHeight = SDL_ReadLE32(src); biHeight = SDL_ReadLE32(src);
/* biPlanes = */ SDL_ReadLE16(src); /* biPlanes = */ SDL_ReadLE16(src);
@ -164,6 +165,54 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
biClrUsed = SDL_ReadLE32(src); biClrUsed = SDL_ReadLE32(src);
/* biClrImportant = */ SDL_ReadLE32(src); /* biClrImportant = */ SDL_ReadLE32(src);
/* 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now. */
if (biSize == 64) {
/* ignore these extra fields. */
if (biCompression == BI_BITFIELDS) {
/* this value is actually huffman compression in this variant. */
SDL_SetError("Compressed BMP files not supported");
was_error = SDL_TRUE;
goto done;
}
} else {
/* This is complicated. If compression is BI_BITFIELDS, then
we have 3 DWORDS that specify the RGB masks. This is either
stored here in an BITMAPV2INFOHEADER (which only differs in
that it adds these RGB masks) and biSize >= 52, or we've got
these masks stored in the exact same place, but strictly
speaking, this is the bmiColors field in BITMAPINFO immediately
following the legacy v1 info header, just past biSize. */
if (biCompression == BI_BITFIELDS) {
haveRGBMasks = SDL_TRUE;
Rmask = SDL_ReadLE32(src);
Gmask = SDL_ReadLE32(src);
Bmask = SDL_ReadLE32(src);
/* ...v3 adds an alpha mask. */
if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
haveAlphaMask = SDL_TRUE;
Amask = SDL_ReadLE32(src);
}
} else {
/* the mask fields are ignored for v2+ headers if not BI_BITFIELD. */
if (biSize >= 52) { /* BITMAPV2INFOHEADER; adds RGB masks */
/*Rmask = */ SDL_ReadLE32(src);
/*Gmask = */ SDL_ReadLE32(src);
/*Bmask = */ SDL_ReadLE32(src);
}
if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
/*Amask = */ SDL_ReadLE32(src);
}
}
/* Insert other fields here; Wikipedia and MSDN say we're up to
v5 of this header, but we ignore those for now (they add gamma,
color spaces, etc). Ignoring the weird OS/2 2.x format, we
currently parse up to v3 correctly (hopefully!). */
}
/* skip any header bytes we didn't handle... */
headerSize = (Uint32) (SDL_RWtell(src) - (fp_offset + 14));
if (biSize > headerSize) { if (biSize > headerSize) {
SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR); SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR);
} }
@ -194,80 +243,46 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
} }
/* We don't support any BMP compression right now */ /* We don't support any BMP compression right now */
Rmask = Gmask = Bmask = Amask = 0;
switch (biCompression) { switch (biCompression) {
case BI_RGB: case BI_RGB:
/* If there are no masks, use the defaults */ /* If there are no masks, use the defaults */
if (bfOffBits == (14 + biSize)) { SDL_assert(!haveRGBMasks);
/* Default values for the BMP format */ SDL_assert(!haveAlphaMask);
switch (biBitCount) { /* Default values for the BMP format */
case 15:
case 16:
Rmask = 0x7C00;
Gmask = 0x03E0;
Bmask = 0x001F;
break;
case 24:
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Rmask = 0x000000FF;
Gmask = 0x0000FF00;
Bmask = 0x00FF0000;
#else
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
#endif
break;
case 32:
/* We don't know if this has alpha channel or not */
correctAlpha = SDL_TRUE;
Amask = 0xFF000000;
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
break;
default:
break;
}
break;
}
/* Fall through -- read the RGB masks */
case BI_BITFIELDS:
switch (biBitCount) { switch (biBitCount) {
case 15: case 15:
case 16: case 16:
Rmask = SDL_ReadLE32(src); Rmask = 0x7C00;
Gmask = SDL_ReadLE32(src); Gmask = 0x03E0;
Bmask = SDL_ReadLE32(src); Bmask = 0x001F;
break;
case 24:
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Rmask = 0x000000FF;
Gmask = 0x0000FF00;
Bmask = 0x00FF0000;
#else
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
#endif
break; break;
case 32: case 32:
Rmask = SDL_ReadLE32(src); /* We don't know if this has alpha channel or not */
Gmask = SDL_ReadLE32(src); correctAlpha = SDL_TRUE;
Bmask = SDL_ReadLE32(src); Amask = 0xFF000000;
Amask = SDL_ReadLE32(src); Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
/* ImageMagick seems to put out bogus masks here. Pick a default. */ Bmask = 0x000000FF;
if ((Rmask == 0xFFFFFF) && (Gmask == 0xFFFFFF) &&
(Bmask == 0xFFFFFF) && (Amask == 0xFFFFFF) ) {
Amask = 0xFF000000;
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
} else if ((Rmask == 0xFFFFFF00) && (Gmask == 0xFFFFFF00) &&
(Bmask == 0xFFFFFF00) && (Amask == 0xFFFFFF00) ) {
/* argh, The Gimp seems to put out different bogus masks! */
Amask = 0x000000FF;
Rmask = 0xFF000000;
Gmask = 0x00FF0000;
Bmask = 0x0000FF00;
}
break; break;
default: default:
break; break;
} }
break; break;
case BI_BITFIELDS:
break; /* we handled this in the info header. */
default: default:
SDL_SetError("Compressed BMP files not supported"); SDL_SetError("Compressed BMP files not supported");
was_error = SDL_TRUE; was_error = SDL_TRUE;