src/video/SDL_bmp.c
author Alex Szpakowski <slime73@gmail.com>
Thu, 15 Jan 2015 01:15:24 -0400
branchiOS-improvements
changeset 9533 e5693e855338
parent 9126 08f3b56969b1
child 9334 5eb5ab33286e
permissions -rw-r--r--
Merged default into iOS-improvements
     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             palette->ncolors = biClrUsed;
   310             palette->colors =
   311                 (SDL_Color *) SDL_realloc(palette->colors,
   312                                           palette->ncolors *
   313                                           sizeof(*palette->colors));
   314             if (!palette->colors) {
   315                 SDL_OutOfMemory();
   316                 was_error = SDL_TRUE;
   317                 goto done;
   318             }
   319         } else if ((int) biClrUsed < palette->ncolors) {
   320             palette->ncolors = biClrUsed;
   321         }
   322         if (biSize == 12) {
   323             for (i = 0; i < (int) biClrUsed; ++i) {
   324                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   325                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   326                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   327                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   328             }
   329         } else {
   330             for (i = 0; i < (int) biClrUsed; ++i) {
   331                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   332                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   333                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   334                 SDL_RWread(src, &palette->colors[i].a, 1, 1);
   335 
   336                 /* According to Microsoft documentation, the fourth element
   337                    is reserved and must be zero, so we shouldn't treat it as
   338                    alpha.
   339                 */
   340                 palette->colors[i].a = SDL_ALPHA_OPAQUE;
   341             }
   342         }
   343     }
   344 
   345     /* Read the surface pixels.  Note that the bmp image is upside down */
   346     if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   347         SDL_Error(SDL_EFSEEK);
   348         was_error = SDL_TRUE;
   349         goto done;
   350     }
   351     top = (Uint8 *)surface->pixels;
   352     end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   353     switch (ExpandBMP) {
   354     case 1:
   355         bmpPitch = (biWidth + 7) >> 3;
   356         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   357         break;
   358     case 4:
   359         bmpPitch = (biWidth + 1) >> 1;
   360         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   361         break;
   362     default:
   363         pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
   364         break;
   365     }
   366     if (topDown) {
   367         bits = top;
   368     } else {
   369         bits = end - surface->pitch;
   370     }
   371     while (bits >= top && bits < end) {
   372         switch (ExpandBMP) {
   373         case 1:
   374         case 4:{
   375                 Uint8 pixel = 0;
   376                 int shift = (8 - ExpandBMP);
   377                 for (i = 0; i < surface->w; ++i) {
   378                     if (i % (8 / ExpandBMP) == 0) {
   379                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   380                             SDL_SetError("Error reading from BMP");
   381                             was_error = SDL_TRUE;
   382                             goto done;
   383                         }
   384                     }
   385                     *(bits + i) = (pixel >> shift);
   386                     pixel <<= ExpandBMP;
   387                 }
   388             }
   389             break;
   390 
   391         default:
   392             if (SDL_RWread(src, bits, 1, surface->pitch)
   393                 != surface->pitch) {
   394                 SDL_Error(SDL_EFREAD);
   395                 was_error = SDL_TRUE;
   396                 goto done;
   397             }
   398 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   399             /* Byte-swap the pixels if needed. Note that the 24bpp
   400                case has already been taken care of above. */
   401             switch (biBitCount) {
   402             case 15:
   403             case 16:{
   404                     Uint16 *pix = (Uint16 *) bits;
   405                     for (i = 0; i < surface->w; i++)
   406                         pix[i] = SDL_Swap16(pix[i]);
   407                     break;
   408                 }
   409 
   410             case 32:{
   411                     Uint32 *pix = (Uint32 *) bits;
   412                     for (i = 0; i < surface->w; i++)
   413                         pix[i] = SDL_Swap32(pix[i]);
   414                     break;
   415                 }
   416             }
   417 #endif
   418             break;
   419         }
   420         /* Skip padding bytes, ugh */
   421         if (pad) {
   422             Uint8 padbyte;
   423             for (i = 0; i < pad; ++i) {
   424                 SDL_RWread(src, &padbyte, 1, 1);
   425             }
   426         }
   427         if (topDown) {
   428             bits += surface->pitch;
   429         } else {
   430             bits -= surface->pitch;
   431         }
   432     }
   433     if (correctAlpha) {
   434         CorrectAlphaChannel(surface);
   435     }
   436   done:
   437     if (was_error) {
   438         if (src) {
   439             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   440         }
   441         SDL_FreeSurface(surface);
   442         surface = NULL;
   443     }
   444     if (freesrc && src) {
   445         SDL_RWclose(src);
   446     }
   447     return (surface);
   448 }
   449 
   450 int
   451 SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
   452 {
   453     Sint64 fp_offset;
   454     int i, pad;
   455     SDL_Surface *surface;
   456     Uint8 *bits;
   457 
   458     /* The Win32 BMP file header (14 bytes) */
   459     char magic[2] = { 'B', 'M' };
   460     Uint32 bfSize;
   461     Uint16 bfReserved1;
   462     Uint16 bfReserved2;
   463     Uint32 bfOffBits;
   464 
   465     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   466     Uint32 biSize;
   467     Sint32 biWidth;
   468     Sint32 biHeight;
   469     Uint16 biPlanes;
   470     Uint16 biBitCount;
   471     Uint32 biCompression;
   472     Uint32 biSizeImage;
   473     Sint32 biXPelsPerMeter;
   474     Sint32 biYPelsPerMeter;
   475     Uint32 biClrUsed;
   476     Uint32 biClrImportant;
   477 
   478     /* Make sure we have somewhere to save */
   479     surface = NULL;
   480     if (dst) {
   481         SDL_bool save32bit = SDL_FALSE;
   482 #ifdef SAVE_32BIT_BMP
   483         /* We can save alpha information in a 32-bit BMP */
   484         if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
   485             saveme->format->Amask) {
   486             save32bit = SDL_TRUE;
   487         }
   488 #endif /* SAVE_32BIT_BMP */
   489 
   490         if (saveme->format->palette && !save32bit) {
   491             if (saveme->format->BitsPerPixel == 8) {
   492                 surface = saveme;
   493             } else {
   494                 SDL_SetError("%d bpp BMP files not supported",
   495                              saveme->format->BitsPerPixel);
   496             }
   497         } else if ((saveme->format->BitsPerPixel == 24) &&
   498 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   499                    (saveme->format->Rmask == 0x00FF0000) &&
   500                    (saveme->format->Gmask == 0x0000FF00) &&
   501                    (saveme->format->Bmask == 0x000000FF)
   502 #else
   503                    (saveme->format->Rmask == 0x000000FF) &&
   504                    (saveme->format->Gmask == 0x0000FF00) &&
   505                    (saveme->format->Bmask == 0x00FF0000)
   506 #endif
   507             ) {
   508             surface = saveme;
   509         } else {
   510             SDL_PixelFormat format;
   511 
   512             /* If the surface has a colorkey or alpha channel we'll save a
   513                32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
   514             if (save32bit) {
   515                 SDL_InitFormat(&format,
   516 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   517                                SDL_PIXELFORMAT_ARGB8888
   518 #else
   519                                SDL_PIXELFORMAT_BGRA8888
   520 #endif
   521                                );
   522             } else {
   523                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
   524             }
   525             surface = SDL_ConvertSurface(saveme, &format, 0);
   526             if (!surface) {
   527                 SDL_SetError("Couldn't convert image to %d bpp",
   528                              format.BitsPerPixel);
   529             }
   530         }
   531     }
   532 
   533     if (surface && (SDL_LockSurface(surface) == 0)) {
   534         const int bw = surface->w * surface->format->BytesPerPixel;
   535 
   536         /* Set the BMP file header values */
   537         bfSize = 0;             /* We'll write this when we're done */
   538         bfReserved1 = 0;
   539         bfReserved2 = 0;
   540         bfOffBits = 0;          /* We'll write this when we're done */
   541 
   542         /* Write the BMP file header values */
   543         fp_offset = SDL_RWtell(dst);
   544         SDL_ClearError();
   545         SDL_RWwrite(dst, magic, 2, 1);
   546         SDL_WriteLE32(dst, bfSize);
   547         SDL_WriteLE16(dst, bfReserved1);
   548         SDL_WriteLE16(dst, bfReserved2);
   549         SDL_WriteLE32(dst, bfOffBits);
   550 
   551         /* Set the BMP info values */
   552         biSize = 40;
   553         biWidth = surface->w;
   554         biHeight = surface->h;
   555         biPlanes = 1;
   556         biBitCount = surface->format->BitsPerPixel;
   557         biCompression = BI_RGB;
   558         biSizeImage = surface->h * surface->pitch;
   559         biXPelsPerMeter = 0;
   560         biYPelsPerMeter = 0;
   561         if (surface->format->palette) {
   562             biClrUsed = surface->format->palette->ncolors;
   563         } else {
   564             biClrUsed = 0;
   565         }
   566         biClrImportant = 0;
   567 
   568         /* Write the BMP info values */
   569         SDL_WriteLE32(dst, biSize);
   570         SDL_WriteLE32(dst, biWidth);
   571         SDL_WriteLE32(dst, biHeight);
   572         SDL_WriteLE16(dst, biPlanes);
   573         SDL_WriteLE16(dst, biBitCount);
   574         SDL_WriteLE32(dst, biCompression);
   575         SDL_WriteLE32(dst, biSizeImage);
   576         SDL_WriteLE32(dst, biXPelsPerMeter);
   577         SDL_WriteLE32(dst, biYPelsPerMeter);
   578         SDL_WriteLE32(dst, biClrUsed);
   579         SDL_WriteLE32(dst, biClrImportant);
   580 
   581         /* Write the palette (in BGR color order) */
   582         if (surface->format->palette) {
   583             SDL_Color *colors;
   584             int ncolors;
   585 
   586             colors = surface->format->palette->colors;
   587             ncolors = surface->format->palette->ncolors;
   588             for (i = 0; i < ncolors; ++i) {
   589                 SDL_RWwrite(dst, &colors[i].b, 1, 1);
   590                 SDL_RWwrite(dst, &colors[i].g, 1, 1);
   591                 SDL_RWwrite(dst, &colors[i].r, 1, 1);
   592                 SDL_RWwrite(dst, &colors[i].a, 1, 1);
   593             }
   594         }
   595 
   596         /* Write the bitmap offset */
   597         bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
   598         if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
   599             SDL_Error(SDL_EFSEEK);
   600         }
   601         SDL_WriteLE32(dst, bfOffBits);
   602         if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   603             SDL_Error(SDL_EFSEEK);
   604         }
   605 
   606         /* Write the bitmap image upside down */
   607         bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   608         pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
   609         while (bits > (Uint8 *) surface->pixels) {
   610             bits -= surface->pitch;
   611             if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
   612                 SDL_Error(SDL_EFWRITE);
   613                 break;
   614             }
   615             if (pad) {
   616                 const Uint8 padbyte = 0;
   617                 for (i = 0; i < pad; ++i) {
   618                     SDL_RWwrite(dst, &padbyte, 1, 1);
   619                 }
   620             }
   621         }
   622 
   623         /* Write the BMP file size */
   624         bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
   625         if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
   626             SDL_Error(SDL_EFSEEK);
   627         }
   628         SDL_WriteLE32(dst, bfSize);
   629         if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
   630             SDL_Error(SDL_EFSEEK);
   631         }
   632 
   633         /* Close it up.. */
   634         SDL_UnlockSurface(surface);
   635         if (surface != saveme) {
   636             SDL_FreeSurface(surface);
   637         }
   638     }
   639 
   640     if (freedst && dst) {
   641         SDL_RWclose(dst);
   642     }
   643     return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
   644 }
   645 
   646 /* vi: set ts=4 sw=4 expandtab: */