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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
diff --git a/src/video/SDL_bmp.c b/src/video/SDL_bmp.c
index e151d20..fcc48c6 100644
--- a/src/video/SDL_bmp.c
+++ b/src/video/SDL_bmp.c
@@ -85,15 +85,17 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
int bmpPitch;
int i, pad;
SDL_Surface *surface;
- Uint32 Rmask;
- Uint32 Gmask;
- Uint32 Bmask;
- Uint32 Amask;
+ Uint32 Rmask = 0;
+ Uint32 Gmask = 0;
+ Uint32 Bmask = 0;
+ Uint32 Amask = 0;
SDL_Palette *palette;
Uint8 *bits;
Uint8 *top, *end;
SDL_bool topDown;
int ExpandBMP;
+ SDL_bool haveRGBMasks = SDL_FALSE;
+ SDL_bool haveAlphaMask = SDL_FALSE;
SDL_bool correctAlpha = SDL_FALSE;
/* The Win32 BMP file header (14 bytes) */
@@ -144,15 +146,14 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
/* Read the Win32 BITMAPINFOHEADER */
biSize = SDL_ReadLE32(src);
- if (biSize == 12) {
+ if (biSize == 12) { /* really old BITMAPCOREHEADER */
biWidth = (Uint32) SDL_ReadLE16(src);
biHeight = (Uint32) SDL_ReadLE16(src);
/* biPlanes = */ SDL_ReadLE16(src);
biBitCount = SDL_ReadLE16(src);
biCompression = BI_RGB;
- } else {
- const unsigned int headerSize = 40;
-
+ } else if (biSize >= 40) { /* some version of BITMAPINFOHEADER */
+ Uint32 headerSize;
biWidth = SDL_ReadLE32(src);
biHeight = SDL_ReadLE32(src);
/* biPlanes = */ SDL_ReadLE16(src);
@@ -164,6 +165,54 @@ SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
biClrUsed = 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) {
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 */
- Rmask = Gmask = Bmask = Amask = 0;
switch (biCompression) {
case BI_RGB:
/* If there are no masks, use the defaults */
- if (bfOffBits == (14 + biSize)) {
- /* Default values for the BMP format */
- switch (biBitCount) {
- 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:
+ SDL_assert(!haveRGBMasks);
+ SDL_assert(!haveAlphaMask);
+ /* Default values for the BMP format */
switch (biBitCount) {
case 15:
case 16:
- Rmask = SDL_ReadLE32(src);
- Gmask = SDL_ReadLE32(src);
- Bmask = SDL_ReadLE32(src);
+ 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:
- Rmask = SDL_ReadLE32(src);
- Gmask = SDL_ReadLE32(src);
- Bmask = SDL_ReadLE32(src);
- Amask = SDL_ReadLE32(src);
-
- /* ImageMagick seems to put out bogus masks here. Pick a default. */
- 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;
- }
-
+ /* 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;
+
+ case BI_BITFIELDS:
+ break; /* we handled this in the info header. */
+
default:
SDL_SetError("Compressed BMP files not supported");
was_error = SDL_TRUE;