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