src/video/SDL_bmp.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 23 Oct 2018 01:34:03 -0400
branchSDL-ryan-batching-renderer
changeset 12352 2b1707cc57fc
parent 11811 5d94cb6b24d3
child 12503 806492103856
permissions -rw-r--r--
render: Add floating point versions of various draw APIs.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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         }
   317         if ((int) biClrUsed > palette->ncolors) {
   318             SDL_Color *colors;
   319             int ncolors = biClrUsed;
   320             colors =
   321                 (SDL_Color *) SDL_realloc(palette->colors,
   322                                           ncolors *
   323                                           sizeof(*palette->colors));
   324             if (!colors) {
   325                 SDL_OutOfMemory();
   326                 was_error = SDL_TRUE;
   327                 goto done;
   328             }
   329             palette->ncolors = ncolors;
   330             palette->colors = colors;
   331         } else if ((int) biClrUsed < palette->ncolors) {
   332             palette->ncolors = biClrUsed;
   333         }
   334         if (biSize == 12) {
   335             for (i = 0; i < (int) biClrUsed; ++i) {
   336                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   337                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   338                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   339                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   340             }
   341         } else {
   342             for (i = 0; i < (int) biClrUsed; ++i) {
   343                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   344                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   345                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   346                 SDL_RWread(src, &palette->colors[i].a, 1, 1);
   347 
   348                 /* According to Microsoft documentation, the fourth element
   349                    is reserved and must be zero, so we shouldn't treat it as
   350                    alpha.
   351                 */
   352                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   353             }
   354         }
   355     }
   356 
   357     /* Read the surface pixels.  Note that the bmp image is upside down */
   358     if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   359         SDL_Error(SDL_EFSEEK);
   360         was_error = SDL_TRUE;
   361         goto done;
   362     }
   363     top = (Uint8 *)surface->pixels;
   364     end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   365     switch (ExpandBMP) {
   366     case 1:
   367         bmpPitch = (biWidth + 7) >> 3;
   368         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   369         break;
   370     case 4:
   371         bmpPitch = (biWidth + 1) >> 1;
   372         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   373         break;
   374     default:
   375         pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
   376         break;
   377     }
   378     if (topDown) {
   379         bits = top;
   380     } else {
   381         bits = end - surface->pitch;
   382     }
   383     while (bits >= top && bits < end) {
   384         switch (ExpandBMP) {
   385         case 1:
   386         case 4:{
   387                 Uint8 pixel = 0;
   388                 int shift = (8 - ExpandBMP);
   389                 for (i = 0; i < surface->w; ++i) {
   390                     if (i % (8 / ExpandBMP) == 0) {
   391                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   392                             SDL_SetError("Error reading from BMP");
   393                             was_error = SDL_TRUE;
   394                             goto done;
   395                         }
   396                     }
   397                     *(bits + i) = (pixel >> shift);
   398                     pixel <<= ExpandBMP;
   399                 }
   400             }
   401             break;
   402 
   403         default:
   404             if (SDL_RWread(src, bits, 1, surface->pitch)
   405                 != surface->pitch) {
   406                 SDL_Error(SDL_EFREAD);
   407                 was_error = SDL_TRUE;
   408                 goto done;
   409             }
   410 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   411             /* Byte-swap the pixels if needed. Note that the 24bpp
   412                case has already been taken care of above. */
   413             switch (biBitCount) {
   414             case 15:
   415             case 16:{
   416                     Uint16 *pix = (Uint16 *) bits;
   417                     for (i = 0; i < surface->w; i++)
   418                         pix[i] = SDL_Swap16(pix[i]);
   419                     break;
   420                 }
   421 
   422             case 32:{
   423                     Uint32 *pix = (Uint32 *) bits;
   424                     for (i = 0; i < surface->w; i++)
   425                         pix[i] = SDL_Swap32(pix[i]);
   426                     break;
   427                 }
   428             }
   429 #endif
   430             break;
   431         }
   432         /* Skip padding bytes, ugh */
   433         if (pad) {
   434             Uint8 padbyte;
   435             for (i = 0; i < pad; ++i) {
   436                 SDL_RWread(src, &padbyte, 1, 1);
   437             }
   438         }
   439         if (topDown) {
   440             bits += surface->pitch;
   441         } else {
   442             bits -= surface->pitch;
   443         }
   444     }
   445     if (correctAlpha) {
   446         CorrectAlphaChannel(surface);
   447     }
   448   done:
   449     if (was_error) {
   450         if (src) {
   451             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   452         }
   453         SDL_FreeSurface(surface);
   454         surface = NULL;
   455     }
   456     if (freesrc && src) {
   457         SDL_RWclose(src);
   458     }
   459     return (surface);
   460 }
   461 
   462 int
   463 SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
   464 {
   465     Sint64 fp_offset;
   466     int i, pad;
   467     SDL_Surface *surface;
   468     Uint8 *bits;
   469     SDL_bool save32bit = SDL_FALSE;
   470     SDL_bool saveLegacyBMP = SDL_FALSE;
   471 
   472     /* The Win32 BMP file header (14 bytes) */
   473     char magic[2] = { 'B', 'M' };
   474     Uint32 bfSize;
   475     Uint16 bfReserved1;
   476     Uint16 bfReserved2;
   477     Uint32 bfOffBits;
   478 
   479     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   480     Uint32 biSize;
   481     Sint32 biWidth;
   482     Sint32 biHeight;
   483     Uint16 biPlanes;
   484     Uint16 biBitCount;
   485     Uint32 biCompression;
   486     Uint32 biSizeImage;
   487     Sint32 biXPelsPerMeter;
   488     Sint32 biYPelsPerMeter;
   489     Uint32 biClrUsed;
   490     Uint32 biClrImportant;
   491 
   492     /* The additional header members from the Win32 BITMAPV4HEADER struct (108 bytes in total) */
   493     Uint32 bV4RedMask = 0;
   494     Uint32 bV4GreenMask = 0;
   495     Uint32 bV4BlueMask = 0;
   496     Uint32 bV4AlphaMask = 0;
   497     Uint32 bV4CSType = 0;
   498     Sint32 bV4Endpoints[3 * 3] = {0};
   499     Uint32 bV4GammaRed = 0;
   500     Uint32 bV4GammaGreen = 0;
   501     Uint32 bV4GammaBlue = 0;
   502 
   503     /* Make sure we have somewhere to save */
   504     surface = NULL;
   505     if (dst) {
   506 #ifdef SAVE_32BIT_BMP
   507         /* We can save alpha information in a 32-bit BMP */
   508         if (saveme->format->BitsPerPixel >= 8 && (saveme->format->Amask ||
   509             saveme->map->info.flags & SDL_COPY_COLORKEY)) {
   510             save32bit = SDL_TRUE;
   511         }
   512 #endif /* SAVE_32BIT_BMP */
   513 
   514         if (saveme->format->palette && !save32bit) {
   515             if (saveme->format->BitsPerPixel == 8) {
   516                 surface = saveme;
   517             } else {
   518                 SDL_SetError("%d bpp BMP files not supported",
   519                              saveme->format->BitsPerPixel);
   520             }
   521         } else if ((saveme->format->BitsPerPixel == 24) && !save32bit &&
   522 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   523                    (saveme->format->Rmask == 0x00FF0000) &&
   524                    (saveme->format->Gmask == 0x0000FF00) &&
   525                    (saveme->format->Bmask == 0x000000FF)
   526 #else
   527                    (saveme->format->Rmask == 0x000000FF) &&
   528                    (saveme->format->Gmask == 0x0000FF00) &&
   529                    (saveme->format->Bmask == 0x00FF0000)
   530 #endif
   531             ) {
   532             surface = saveme;
   533         } else {
   534             SDL_PixelFormat format;
   535 
   536             /* If the surface has a colorkey or alpha channel we'll save a
   537                32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
   538             if (save32bit) {
   539                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGRA32);
   540             } else {
   541                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
   542             }
   543             surface = SDL_ConvertSurface(saveme, &format, 0);
   544             if (!surface) {
   545                 SDL_SetError("Couldn't convert image to %d bpp",
   546                              format.BitsPerPixel);
   547             }
   548         }
   549     } else {
   550         /* Set no error here because it may overwrite a more useful message from
   551            SDL_RWFromFile() if SDL_SaveBMP_RW() is called from SDL_SaveBMP(). */
   552         return -1;
   553     }
   554 
   555     if (save32bit) {
   556         saveLegacyBMP = SDL_GetHintBoolean(SDL_HINT_BMP_SAVE_LEGACY_FORMAT, SDL_FALSE);
   557     }
   558 
   559     if (surface && (SDL_LockSurface(surface) == 0)) {
   560         const int bw = surface->w * surface->format->BytesPerPixel;
   561 
   562         /* Set the BMP file header values */
   563         bfSize = 0;             /* We'll write this when we're done */
   564         bfReserved1 = 0;
   565         bfReserved2 = 0;
   566         bfOffBits = 0;          /* We'll write this when we're done */
   567 
   568         /* Write the BMP file header values */
   569         fp_offset = SDL_RWtell(dst);
   570         SDL_ClearError();
   571         SDL_RWwrite(dst, magic, 2, 1);
   572         SDL_WriteLE32(dst, bfSize);
   573         SDL_WriteLE16(dst, bfReserved1);
   574         SDL_WriteLE16(dst, bfReserved2);
   575         SDL_WriteLE32(dst, bfOffBits);
   576 
   577         /* Set the BMP info values */
   578         biSize = 40;
   579         biWidth = surface->w;
   580         biHeight = surface->h;
   581         biPlanes = 1;
   582         biBitCount = surface->format->BitsPerPixel;
   583         biCompression = BI_RGB;
   584         biSizeImage = surface->h * surface->pitch;
   585         biXPelsPerMeter = 0;
   586         biYPelsPerMeter = 0;
   587         if (surface->format->palette) {
   588             biClrUsed = surface->format->palette->ncolors;
   589         } else {
   590             biClrUsed = 0;
   591         }
   592         biClrImportant = 0;
   593 
   594         /* Set the BMP info values for the version 4 header */
   595         if (save32bit && !saveLegacyBMP) {
   596             biSize = 108;
   597             biCompression = BI_BITFIELDS;
   598             /* The BMP format is always little endian, these masks stay the same */
   599             bV4RedMask   = 0x00ff0000;
   600             bV4GreenMask = 0x0000ff00;
   601             bV4BlueMask  = 0x000000ff;
   602             bV4AlphaMask = 0xff000000;
   603             bV4CSType = LCS_WINDOWS_COLOR_SPACE;
   604             bV4GammaRed = 0;
   605             bV4GammaGreen = 0;
   606             bV4GammaBlue = 0;
   607         }
   608 
   609         /* Write the BMP info values */
   610         SDL_WriteLE32(dst, biSize);
   611         SDL_WriteLE32(dst, biWidth);
   612         SDL_WriteLE32(dst, biHeight);
   613         SDL_WriteLE16(dst, biPlanes);
   614         SDL_WriteLE16(dst, biBitCount);
   615         SDL_WriteLE32(dst, biCompression);
   616         SDL_WriteLE32(dst, biSizeImage);
   617         SDL_WriteLE32(dst, biXPelsPerMeter);
   618         SDL_WriteLE32(dst, biYPelsPerMeter);
   619         SDL_WriteLE32(dst, biClrUsed);
   620         SDL_WriteLE32(dst, biClrImportant);
   621 
   622         /* Write the BMP info values for the version 4 header */
   623         if (save32bit && !saveLegacyBMP) {
   624             SDL_WriteLE32(dst, bV4RedMask);
   625             SDL_WriteLE32(dst, bV4GreenMask);
   626             SDL_WriteLE32(dst, bV4BlueMask);
   627             SDL_WriteLE32(dst, bV4AlphaMask);
   628             SDL_WriteLE32(dst, bV4CSType);
   629             for (i = 0; i < 3 * 3; i++) {
   630                 SDL_WriteLE32(dst, bV4Endpoints[i]);
   631             }
   632             SDL_WriteLE32(dst, bV4GammaRed);
   633             SDL_WriteLE32(dst, bV4GammaGreen);
   634             SDL_WriteLE32(dst, bV4GammaBlue);
   635         }
   636 
   637         /* Write the palette (in BGR color order) */
   638         if (surface->format->palette) {
   639             SDL_Color *colors;
   640             int ncolors;
   641 
   642             colors = surface->format->palette->colors;
   643             ncolors = surface->format->palette->ncolors;
   644             for (i = 0; i < ncolors; ++i) {
   645                 SDL_RWwrite(dst, &colors[i].b, 1, 1);
   646                 SDL_RWwrite(dst, &colors[i].g, 1, 1);
   647                 SDL_RWwrite(dst, &colors[i].r, 1, 1);
   648                 SDL_RWwrite(dst, &colors[i].a, 1, 1);
   649             }
   650         }
   651 
   652         /* Write the bitmap offset */
   653         bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
   654         if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
   655             SDL_Error(SDL_EFSEEK);
   656         }
   657         SDL_WriteLE32(dst, bfOffBits);
   658         if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   659             SDL_Error(SDL_EFSEEK);
   660         }
   661 
   662         /* Write the bitmap image upside down */
   663         bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   664         pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
   665         while (bits > (Uint8 *) surface->pixels) {
   666             bits -= surface->pitch;
   667             if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
   668                 SDL_Error(SDL_EFWRITE);
   669                 break;
   670             }
   671             if (pad) {
   672                 const Uint8 padbyte = 0;
   673                 for (i = 0; i < pad; ++i) {
   674                     SDL_RWwrite(dst, &padbyte, 1, 1);
   675                 }
   676             }
   677         }
   678 
   679         /* Write the BMP file size */
   680         bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
   681         if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
   682             SDL_Error(SDL_EFSEEK);
   683         }
   684         SDL_WriteLE32(dst, bfSize);
   685         if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
   686             SDL_Error(SDL_EFSEEK);
   687         }
   688 
   689         /* Close it up.. */
   690         SDL_UnlockSurface(surface);
   691         if (surface != saveme) {
   692             SDL_FreeSurface(surface);
   693         }
   694     }
   695 
   696     if (freedst && dst) {
   697         SDL_RWclose(dst);
   698     }
   699     return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
   700 }
   701 
   702 /* vi: set ts=4 sw=4 expandtab: */