src/video/SDL_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 18 Feb 2019 07:50:33 -0800
changeset 12612 07c39cbbeacf
parent 12503 806492103856
child 12644 7c643f1c1887
permissions -rw-r--r--
Fixed bug 4500 - Heap-Buffer Overflow in Map1toN pertaining to SDL_pixels.c

Petr Pisar

The reproducer has these data in BITMAPINFOHEADER:

biSize = 40
biBitCount = 8
biClrUsed = 131075

SDL_LoadBMP_RW() function passes biBitCount as a color depth to SDL_CreateRGBSurface(), thus 256-color pallete is allocated. But then biClrUsed colors are read from a file and stored into the palette. SDL_LoadBMP_RW should report an error if biClrUsed is greater than 2^biBitCount.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../SDL_internal.h"
    22 
    23 /*
    24    Code to load and save surfaces in Windows BMP format.
    25 
    26    Why support BMP format?  Well, it's a native format for Windows, and
    27    most image processing programs can read and write it.  It would be nice
    28    to be able to have at least one image format that we can natively load
    29    and save, and since PNG is so complex that it would bloat the library,
    30    BMP is a good alternative.
    31 
    32    This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
    33 */
    34 
    35 #include "SDL_hints.h"
    36 #include "SDL_video.h"
    37 #include "SDL_assert.h"
    38 #include "SDL_endian.h"
    39 #include "SDL_pixels_c.h"
    40 
    41 #define SAVE_32BIT_BMP
    42 
    43 /* Compression encodings for BMP files */
    44 #ifndef BI_RGB
    45 #define BI_RGB      0
    46 #define BI_RLE8     1
    47 #define BI_RLE4     2
    48 #define BI_BITFIELDS    3
    49 #endif
    50 
    51 /* Logical color space values for BMP files */
    52 #ifndef LCS_WINDOWS_COLOR_SPACE
    53 /* 0x57696E20 == "Win " */
    54 #define LCS_WINDOWS_COLOR_SPACE    0x57696E20
    55 #endif
    56 
    57 static void CorrectAlphaChannel(SDL_Surface *surface)
    58 {
    59     /* Check to see if there is any alpha channel data */
    60     SDL_bool hasAlpha = SDL_FALSE;
    61 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
    62     int alphaChannelOffset = 0;
    63 #else
    64     int alphaChannelOffset = 3;
    65 #endif
    66     Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
    67     Uint8 *end = alpha + surface->h * surface->pitch;
    68 
    69     while (alpha < end) {
    70         if (*alpha != 0) {
    71             hasAlpha = SDL_TRUE;
    72             break;
    73         }
    74         alpha += 4;
    75     }
    76 
    77     if (!hasAlpha) {
    78         alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
    79         while (alpha < end) {
    80             *alpha = SDL_ALPHA_OPAQUE;
    81             alpha += 4;
    82         }
    83     }
    84 }
    85 
    86 SDL_Surface *
    87 SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
    88 {
    89     SDL_bool was_error;
    90     Sint64 fp_offset = 0;
    91     int bmpPitch;
    92     int i, pad;
    93     SDL_Surface *surface;
    94     Uint32 Rmask = 0;
    95     Uint32 Gmask = 0;
    96     Uint32 Bmask = 0;
    97     Uint32 Amask = 0;
    98     SDL_Palette *palette;
    99     Uint8 *bits;
   100     Uint8 *top, *end;
   101     SDL_bool topDown;
   102     int ExpandBMP;
   103     SDL_bool haveRGBMasks = SDL_FALSE;
   104     SDL_bool haveAlphaMask = SDL_FALSE;
   105     SDL_bool correctAlpha = SDL_FALSE;
   106 
   107     /* The Win32 BMP file header (14 bytes) */
   108     char magic[2];
   109     /* Uint32 bfSize = 0; */
   110     /* Uint16 bfReserved1 = 0; */
   111     /* Uint16 bfReserved2 = 0; */
   112     Uint32 bfOffBits = 0;
   113 
   114     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   115     Uint32 biSize = 0;
   116     Sint32 biWidth = 0;
   117     Sint32 biHeight = 0;
   118     /* Uint16 biPlanes = 0; */
   119     Uint16 biBitCount = 0;
   120     Uint32 biCompression = 0;
   121     /* Uint32 biSizeImage = 0; */
   122     /* Sint32 biXPelsPerMeter = 0; */
   123     /* Sint32 biYPelsPerMeter = 0; */
   124     Uint32 biClrUsed = 0;
   125     /* Uint32 biClrImportant = 0; */
   126 
   127     (void) haveRGBMasks;
   128     (void) haveAlphaMask;
   129 
   130     /* Make sure we are passed a valid data source */
   131     surface = NULL;
   132     was_error = SDL_FALSE;
   133     if (src == NULL) {
   134         was_error = SDL_TRUE;
   135         goto done;
   136     }
   137 
   138     /* Read in the BMP file header */
   139     fp_offset = SDL_RWtell(src);
   140     SDL_ClearError();
   141     if (SDL_RWread(src, magic, 1, 2) != 2) {
   142         SDL_Error(SDL_EFREAD);
   143         was_error = SDL_TRUE;
   144         goto done;
   145     }
   146     if (SDL_strncmp(magic, "BM", 2) != 0) {
   147         SDL_SetError("File is not a Windows BMP file");
   148         was_error = SDL_TRUE;
   149         goto done;
   150     }
   151     /* bfSize = */ SDL_ReadLE32(src);
   152     /* bfReserved1 = */ SDL_ReadLE16(src);
   153     /* bfReserved2 = */ SDL_ReadLE16(src);
   154     bfOffBits = SDL_ReadLE32(src);
   155 
   156     /* Read the Win32 BITMAPINFOHEADER */
   157     biSize = SDL_ReadLE32(src);
   158     if (biSize == 12) {   /* really old BITMAPCOREHEADER */
   159         biWidth = (Uint32) SDL_ReadLE16(src);
   160         biHeight = (Uint32) SDL_ReadLE16(src);
   161         /* biPlanes = */ SDL_ReadLE16(src);
   162         biBitCount = SDL_ReadLE16(src);
   163         biCompression = BI_RGB;
   164     } else if (biSize >= 40) {  /* some version of BITMAPINFOHEADER */
   165         Uint32 headerSize;
   166         biWidth = SDL_ReadLE32(src);
   167         biHeight = SDL_ReadLE32(src);
   168         /* biPlanes = */ SDL_ReadLE16(src);
   169         biBitCount = SDL_ReadLE16(src);
   170         biCompression = SDL_ReadLE32(src);
   171         /* biSizeImage = */ SDL_ReadLE32(src);
   172         /* biXPelsPerMeter = */ SDL_ReadLE32(src);
   173         /* biYPelsPerMeter = */ SDL_ReadLE32(src);
   174         biClrUsed = SDL_ReadLE32(src);
   175         /* biClrImportant = */ SDL_ReadLE32(src);
   176 
   177         /* 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now. */
   178         if (biSize == 64) {
   179             /* ignore these extra fields. */
   180             if (biCompression == BI_BITFIELDS) {
   181                 /* this value is actually huffman compression in this variant. */
   182                 SDL_SetError("Compressed BMP files not supported");
   183                 was_error = SDL_TRUE;
   184                 goto done;
   185             }
   186         } else {
   187             /* This is complicated. If compression is BI_BITFIELDS, then
   188                we have 3 DWORDS that specify the RGB masks. This is either
   189                stored here in an BITMAPV2INFOHEADER (which only differs in
   190                that it adds these RGB masks) and biSize >= 52, or we've got
   191                these masks stored in the exact same place, but strictly
   192                speaking, this is the bmiColors field in BITMAPINFO immediately
   193                following the legacy v1 info header, just past biSize. */
   194             if (biCompression == BI_BITFIELDS) {
   195                 haveRGBMasks = SDL_TRUE;
   196                 Rmask = SDL_ReadLE32(src);
   197                 Gmask = SDL_ReadLE32(src);
   198                 Bmask = SDL_ReadLE32(src);
   199 
   200                 /* ...v3 adds an alpha mask. */
   201                 if (biSize >= 56) {  /* BITMAPV3INFOHEADER; adds alpha mask */
   202                     haveAlphaMask = SDL_TRUE;
   203                     Amask = SDL_ReadLE32(src);
   204                 }
   205             } else {
   206                 /* the mask fields are ignored for v2+ headers if not BI_BITFIELD. */
   207                 if (biSize >= 52) {  /* BITMAPV2INFOHEADER; adds RGB masks */
   208                     /*Rmask = */ SDL_ReadLE32(src);
   209                     /*Gmask = */ SDL_ReadLE32(src);
   210                     /*Bmask = */ SDL_ReadLE32(src);
   211                 }
   212                 if (biSize >= 56) {  /* BITMAPV3INFOHEADER; adds alpha mask */
   213                     /*Amask = */ SDL_ReadLE32(src);
   214                 }
   215             }
   216 
   217             /* Insert other fields here; Wikipedia and MSDN say we're up to
   218                v5 of this header, but we ignore those for now (they add gamma,
   219                color spaces, etc). Ignoring the weird OS/2 2.x format, we
   220                currently parse up to v3 correctly (hopefully!). */
   221         }
   222 
   223         /* skip any header bytes we didn't handle... */
   224         headerSize = (Uint32) (SDL_RWtell(src) - (fp_offset + 14));
   225         if (biSize > headerSize) {
   226             SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR);
   227         }
   228     }
   229     if (biHeight < 0) {
   230         topDown = SDL_TRUE;
   231         biHeight = -biHeight;
   232     } else {
   233         topDown = SDL_FALSE;
   234     }
   235 
   236     /* Check for read error */
   237     if (SDL_strcmp(SDL_GetError(), "") != 0) {
   238         was_error = SDL_TRUE;
   239         goto done;
   240     }
   241 
   242     /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   243     switch (biBitCount) {
   244     case 1:
   245     case 4:
   246         ExpandBMP = biBitCount;
   247         biBitCount = 8;
   248         break;
   249     default:
   250         ExpandBMP = 0;
   251         break;
   252     }
   253 
   254     /* We don't support any BMP compression right now */
   255     switch (biCompression) {
   256     case BI_RGB:
   257         /* If there are no masks, use the defaults */
   258         SDL_assert(!haveRGBMasks);
   259         SDL_assert(!haveAlphaMask);
   260         /* Default values for the BMP format */
   261         switch (biBitCount) {
   262         case 15:
   263         case 16:
   264             Rmask = 0x7C00;
   265             Gmask = 0x03E0;
   266             Bmask = 0x001F;
   267             break;
   268         case 24:
   269 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   270             Rmask = 0x000000FF;
   271             Gmask = 0x0000FF00;
   272             Bmask = 0x00FF0000;
   273 #else
   274             Rmask = 0x00FF0000;
   275             Gmask = 0x0000FF00;
   276             Bmask = 0x000000FF;
   277 #endif
   278             break;
   279         case 32:
   280             /* We don't know if this has alpha channel or not */
   281             correctAlpha = SDL_TRUE;
   282             Amask = 0xFF000000;
   283             Rmask = 0x00FF0000;
   284             Gmask = 0x0000FF00;
   285             Bmask = 0x000000FF;
   286             break;
   287         default:
   288             break;
   289         }
   290         break;
   291 
   292     case BI_BITFIELDS:
   293         break;  /* we handled this in the info header. */
   294 
   295     default:
   296         SDL_SetError("Compressed BMP files not supported");
   297         was_error = SDL_TRUE;
   298         goto done;
   299     }
   300 
   301     /* Create a compatible surface, note that the colors are RGB ordered */
   302     surface =
   303         SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
   304                              Bmask, Amask);
   305     if (surface == NULL) {
   306         was_error = SDL_TRUE;
   307         goto done;
   308     }
   309 
   310     /* Load the palette, if any */
   311     palette = (surface->format)->palette;
   312     if (palette) {
   313         SDL_assert(biBitCount <= 8);
   314         if (biClrUsed == 0) {
   315             biClrUsed = 1 << biBitCount;
   316 		} else if (biClrUsed > (1 << biBitCount)) {
   317 			SDL_SetError("BMP file has an invalid number of colors");
   318 			was_error = SDL_TRUE;
   319 			goto done;
   320         }
   321         if ((int) biClrUsed > palette->ncolors) {
   322             SDL_Color *colors;
   323             int ncolors = biClrUsed;
   324             colors =
   325                 (SDL_Color *) SDL_realloc(palette->colors,
   326                                           ncolors *
   327                                           sizeof(*palette->colors));
   328             if (!colors) {
   329                 SDL_OutOfMemory();
   330                 was_error = SDL_TRUE;
   331                 goto done;
   332             }
   333             palette->ncolors = ncolors;
   334             palette->colors = colors;
   335         } else if ((int) biClrUsed < palette->ncolors) {
   336             palette->ncolors = biClrUsed;
   337         }
   338         if (biSize == 12) {
   339             for (i = 0; i < (int) biClrUsed; ++i) {
   340                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   341                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   342                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   343                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   344             }
   345         } else {
   346             for (i = 0; i < (int) biClrUsed; ++i) {
   347                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   348                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   349                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   350                 SDL_RWread(src, &palette->colors[i].a, 1, 1);
   351 
   352                 /* According to Microsoft documentation, the fourth element
   353                    is reserved and must be zero, so we shouldn't treat it as
   354                    alpha.
   355                 */
   356                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   357             }
   358         }
   359     }
   360 
   361     /* Read the surface pixels.  Note that the bmp image is upside down */
   362     if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   363         SDL_Error(SDL_EFSEEK);
   364         was_error = SDL_TRUE;
   365         goto done;
   366     }
   367     top = (Uint8 *)surface->pixels;
   368     end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   369     switch (ExpandBMP) {
   370     case 1:
   371         bmpPitch = (biWidth + 7) >> 3;
   372         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   373         break;
   374     case 4:
   375         bmpPitch = (biWidth + 1) >> 1;
   376         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   377         break;
   378     default:
   379         pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
   380         break;
   381     }
   382     if (topDown) {
   383         bits = top;
   384     } else {
   385         bits = end - surface->pitch;
   386     }
   387     while (bits >= top && bits < end) {
   388         switch (ExpandBMP) {
   389         case 1:
   390         case 4:{
   391                 Uint8 pixel = 0;
   392                 int shift = (8 - ExpandBMP);
   393                 for (i = 0; i < surface->w; ++i) {
   394                     if (i % (8 / ExpandBMP) == 0) {
   395                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   396                             SDL_SetError("Error reading from BMP");
   397                             was_error = SDL_TRUE;
   398                             goto done;
   399                         }
   400                     }
   401                     *(bits + i) = (pixel >> shift);
   402                     pixel <<= ExpandBMP;
   403                 }
   404             }
   405             break;
   406 
   407         default:
   408             if (SDL_RWread(src, bits, 1, surface->pitch)
   409                 != surface->pitch) {
   410                 SDL_Error(SDL_EFREAD);
   411                 was_error = SDL_TRUE;
   412                 goto done;
   413             }
   414 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   415             /* Byte-swap the pixels if needed. Note that the 24bpp
   416                case has already been taken care of above. */
   417             switch (biBitCount) {
   418             case 15:
   419             case 16:{
   420                     Uint16 *pix = (Uint16 *) bits;
   421                     for (i = 0; i < surface->w; i++)
   422                         pix[i] = SDL_Swap16(pix[i]);
   423                     break;
   424                 }
   425 
   426             case 32:{
   427                     Uint32 *pix = (Uint32 *) bits;
   428                     for (i = 0; i < surface->w; i++)
   429                         pix[i] = SDL_Swap32(pix[i]);
   430                     break;
   431                 }
   432             }
   433 #endif
   434             break;
   435         }
   436         /* Skip padding bytes, ugh */
   437         if (pad) {
   438             Uint8 padbyte;
   439             for (i = 0; i < pad; ++i) {
   440                 SDL_RWread(src, &padbyte, 1, 1);
   441             }
   442         }
   443         if (topDown) {
   444             bits += surface->pitch;
   445         } else {
   446             bits -= surface->pitch;
   447         }
   448     }
   449     if (correctAlpha) {
   450         CorrectAlphaChannel(surface);
   451     }
   452   done:
   453     if (was_error) {
   454         if (src) {
   455             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   456         }
   457         SDL_FreeSurface(surface);
   458         surface = NULL;
   459     }
   460     if (freesrc && src) {
   461         SDL_RWclose(src);
   462     }
   463     return (surface);
   464 }
   465 
   466 int
   467 SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
   468 {
   469     Sint64 fp_offset;
   470     int i, pad;
   471     SDL_Surface *surface;
   472     Uint8 *bits;
   473     SDL_bool save32bit = SDL_FALSE;
   474     SDL_bool saveLegacyBMP = SDL_FALSE;
   475 
   476     /* The Win32 BMP file header (14 bytes) */
   477     char magic[2] = { 'B', 'M' };
   478     Uint32 bfSize;
   479     Uint16 bfReserved1;
   480     Uint16 bfReserved2;
   481     Uint32 bfOffBits;
   482 
   483     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   484     Uint32 biSize;
   485     Sint32 biWidth;
   486     Sint32 biHeight;
   487     Uint16 biPlanes;
   488     Uint16 biBitCount;
   489     Uint32 biCompression;
   490     Uint32 biSizeImage;
   491     Sint32 biXPelsPerMeter;
   492     Sint32 biYPelsPerMeter;
   493     Uint32 biClrUsed;
   494     Uint32 biClrImportant;
   495 
   496     /* The additional header members from the Win32 BITMAPV4HEADER struct (108 bytes in total) */
   497     Uint32 bV4RedMask = 0;
   498     Uint32 bV4GreenMask = 0;
   499     Uint32 bV4BlueMask = 0;
   500     Uint32 bV4AlphaMask = 0;
   501     Uint32 bV4CSType = 0;
   502     Sint32 bV4Endpoints[3 * 3] = {0};
   503     Uint32 bV4GammaRed = 0;
   504     Uint32 bV4GammaGreen = 0;
   505     Uint32 bV4GammaBlue = 0;
   506 
   507     /* Make sure we have somewhere to save */
   508     surface = NULL;
   509     if (dst) {
   510 #ifdef SAVE_32BIT_BMP
   511         /* We can save alpha information in a 32-bit BMP */
   512         if (saveme->format->BitsPerPixel >= 8 && (saveme->format->Amask ||
   513             saveme->map->info.flags & SDL_COPY_COLORKEY)) {
   514             save32bit = SDL_TRUE;
   515         }
   516 #endif /* SAVE_32BIT_BMP */
   517 
   518         if (saveme->format->palette && !save32bit) {
   519             if (saveme->format->BitsPerPixel == 8) {
   520                 surface = saveme;
   521             } else {
   522                 SDL_SetError("%d bpp BMP files not supported",
   523                              saveme->format->BitsPerPixel);
   524             }
   525         } else if ((saveme->format->BitsPerPixel == 24) && !save32bit &&
   526 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   527                    (saveme->format->Rmask == 0x00FF0000) &&
   528                    (saveme->format->Gmask == 0x0000FF00) &&
   529                    (saveme->format->Bmask == 0x000000FF)
   530 #else
   531                    (saveme->format->Rmask == 0x000000FF) &&
   532                    (saveme->format->Gmask == 0x0000FF00) &&
   533                    (saveme->format->Bmask == 0x00FF0000)
   534 #endif
   535             ) {
   536             surface = saveme;
   537         } else {
   538             SDL_PixelFormat format;
   539 
   540             /* If the surface has a colorkey or alpha channel we'll save a
   541                32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
   542             if (save32bit) {
   543                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGRA32);
   544             } else {
   545                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
   546             }
   547             surface = SDL_ConvertSurface(saveme, &format, 0);
   548             if (!surface) {
   549                 SDL_SetError("Couldn't convert image to %d bpp",
   550                              format.BitsPerPixel);
   551             }
   552         }
   553     } else {
   554         /* Set no error here because it may overwrite a more useful message from
   555            SDL_RWFromFile() if SDL_SaveBMP_RW() is called from SDL_SaveBMP(). */
   556         return -1;
   557     }
   558 
   559     if (save32bit) {
   560         saveLegacyBMP = SDL_GetHintBoolean(SDL_HINT_BMP_SAVE_LEGACY_FORMAT, SDL_FALSE);
   561     }
   562 
   563     if (surface && (SDL_LockSurface(surface) == 0)) {
   564         const int bw = surface->w * surface->format->BytesPerPixel;
   565 
   566         /* Set the BMP file header values */
   567         bfSize = 0;             /* We'll write this when we're done */
   568         bfReserved1 = 0;
   569         bfReserved2 = 0;
   570         bfOffBits = 0;          /* We'll write this when we're done */
   571 
   572         /* Write the BMP file header values */
   573         fp_offset = SDL_RWtell(dst);
   574         SDL_ClearError();
   575         SDL_RWwrite(dst, magic, 2, 1);
   576         SDL_WriteLE32(dst, bfSize);
   577         SDL_WriteLE16(dst, bfReserved1);
   578         SDL_WriteLE16(dst, bfReserved2);
   579         SDL_WriteLE32(dst, bfOffBits);
   580 
   581         /* Set the BMP info values */
   582         biSize = 40;
   583         biWidth = surface->w;
   584         biHeight = surface->h;
   585         biPlanes = 1;
   586         biBitCount = surface->format->BitsPerPixel;
   587         biCompression = BI_RGB;
   588         biSizeImage = surface->h * surface->pitch;
   589         biXPelsPerMeter = 0;
   590         biYPelsPerMeter = 0;
   591         if (surface->format->palette) {
   592             biClrUsed = surface->format->palette->ncolors;
   593         } else {
   594             biClrUsed = 0;
   595         }
   596         biClrImportant = 0;
   597 
   598         /* Set the BMP info values for the version 4 header */
   599         if (save32bit && !saveLegacyBMP) {
   600             biSize = 108;
   601             biCompression = BI_BITFIELDS;
   602             /* The BMP format is always little endian, these masks stay the same */
   603             bV4RedMask   = 0x00ff0000;
   604             bV4GreenMask = 0x0000ff00;
   605             bV4BlueMask  = 0x000000ff;
   606             bV4AlphaMask = 0xff000000;
   607             bV4CSType = LCS_WINDOWS_COLOR_SPACE;
   608             bV4GammaRed = 0;
   609             bV4GammaGreen = 0;
   610             bV4GammaBlue = 0;
   611         }
   612 
   613         /* Write the BMP info values */
   614         SDL_WriteLE32(dst, biSize);
   615         SDL_WriteLE32(dst, biWidth);
   616         SDL_WriteLE32(dst, biHeight);
   617         SDL_WriteLE16(dst, biPlanes);
   618         SDL_WriteLE16(dst, biBitCount);
   619         SDL_WriteLE32(dst, biCompression);
   620         SDL_WriteLE32(dst, biSizeImage);
   621         SDL_WriteLE32(dst, biXPelsPerMeter);
   622         SDL_WriteLE32(dst, biYPelsPerMeter);
   623         SDL_WriteLE32(dst, biClrUsed);
   624         SDL_WriteLE32(dst, biClrImportant);
   625 
   626         /* Write the BMP info values for the version 4 header */
   627         if (save32bit && !saveLegacyBMP) {
   628             SDL_WriteLE32(dst, bV4RedMask);
   629             SDL_WriteLE32(dst, bV4GreenMask);
   630             SDL_WriteLE32(dst, bV4BlueMask);
   631             SDL_WriteLE32(dst, bV4AlphaMask);
   632             SDL_WriteLE32(dst, bV4CSType);
   633             for (i = 0; i < 3 * 3; i++) {
   634                 SDL_WriteLE32(dst, bV4Endpoints[i]);
   635             }
   636             SDL_WriteLE32(dst, bV4GammaRed);
   637             SDL_WriteLE32(dst, bV4GammaGreen);
   638             SDL_WriteLE32(dst, bV4GammaBlue);
   639         }
   640 
   641         /* Write the palette (in BGR color order) */
   642         if (surface->format->palette) {
   643             SDL_Color *colors;
   644             int ncolors;
   645 
   646             colors = surface->format->palette->colors;
   647             ncolors = surface->format->palette->ncolors;
   648             for (i = 0; i < ncolors; ++i) {
   649                 SDL_RWwrite(dst, &colors[i].b, 1, 1);
   650                 SDL_RWwrite(dst, &colors[i].g, 1, 1);
   651                 SDL_RWwrite(dst, &colors[i].r, 1, 1);
   652                 SDL_RWwrite(dst, &colors[i].a, 1, 1);
   653             }
   654         }
   655 
   656         /* Write the bitmap offset */
   657         bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
   658         if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
   659             SDL_Error(SDL_EFSEEK);
   660         }
   661         SDL_WriteLE32(dst, bfOffBits);
   662         if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   663             SDL_Error(SDL_EFSEEK);
   664         }
   665 
   666         /* Write the bitmap image upside down */
   667         bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   668         pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
   669         while (bits > (Uint8 *) surface->pixels) {
   670             bits -= surface->pitch;
   671             if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
   672                 SDL_Error(SDL_EFWRITE);
   673                 break;
   674             }
   675             if (pad) {
   676                 const Uint8 padbyte = 0;
   677                 for (i = 0; i < pad; ++i) {
   678                     SDL_RWwrite(dst, &padbyte, 1, 1);
   679                 }
   680             }
   681         }
   682 
   683         /* Write the BMP file size */
   684         bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
   685         if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
   686             SDL_Error(SDL_EFSEEK);
   687         }
   688         SDL_WriteLE32(dst, bfSize);
   689         if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
   690             SDL_Error(SDL_EFSEEK);
   691         }
   692 
   693         /* Close it up.. */
   694         SDL_UnlockSurface(surface);
   695         if (surface != saveme) {
   696             SDL_FreeSurface(surface);
   697         }
   698     }
   699 
   700     if (freedst && dst) {
   701         SDL_RWclose(dst);
   702     }
   703     return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
   704 }
   705 
   706 /* vi: set ts=4 sw=4 expandtab: */