src/video/SDL_bmp.c
author Andreas Schiffler <aschiffler@ferzkopp.net>
Sun, 30 Oct 2011 00:19:13 -0700
changeset 6037 2feab4874268
parent 5535 96594ac5fd1a
child 6094 5dac1b1261c0
permissions -rw-r--r--
Fix sdl-haiku buildbot compiler warnings
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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_config.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_endian.h"
    37 #include "SDL_pixels_c.h"
    38 
    39 /* Compression encodings for BMP files */
    40 #ifndef BI_RGB
    41 #define BI_RGB		0
    42 #define BI_RLE8		1
    43 #define BI_RLE4		2
    44 #define BI_BITFIELDS	3
    45 #endif
    46 
    47 
    48 SDL_Surface *
    49 SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
    50 {
    51     SDL_bool was_error;
    52     long fp_offset = 0;
    53     int bmpPitch;
    54     int i, pad;
    55     SDL_Surface *surface;
    56     Uint32 Rmask;
    57     Uint32 Gmask;
    58     Uint32 Bmask;
    59     Uint32 Amask;
    60     SDL_Palette *palette;
    61     Uint8 *bits;
    62     Uint8 *top, *end;
    63     SDL_bool topDown;
    64     int ExpandBMP;
    65 
    66     /* The Win32 BMP file header (14 bytes) */
    67     char magic[2];
    68     Uint32 bfSize;
    69     Uint16 bfReserved1;
    70     Uint16 bfReserved2;
    71     Uint32 bfOffBits;
    72 
    73     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
    74     Uint32 biSize;
    75     Sint32 biWidth;
    76     Sint32 biHeight;
    77     Uint16 biPlanes;
    78     Uint16 biBitCount;
    79     Uint32 biCompression;
    80     Uint32 biSizeImage;
    81     Sint32 biXPelsPerMeter;
    82     Sint32 biYPelsPerMeter;
    83     Uint32 biClrUsed;
    84     Uint32 biClrImportant;
    85 
    86     /* Make sure we are passed a valid data source */
    87     surface = NULL;
    88     was_error = SDL_FALSE;
    89     if (src == NULL) {
    90         was_error = SDL_TRUE;
    91         goto done;
    92     }
    93 
    94     /* Read in the BMP file header */
    95     fp_offset = SDL_RWtell(src);
    96     SDL_ClearError();
    97     if (SDL_RWread(src, magic, 1, 2) != 2) {
    98         SDL_Error(SDL_EFREAD);
    99         was_error = SDL_TRUE;
   100         goto done;
   101     }
   102     if (SDL_strncmp(magic, "BM", 2) != 0) {
   103         SDL_SetError("File is not a Windows BMP file");
   104         was_error = SDL_TRUE;
   105         goto done;
   106     }
   107     bfSize = SDL_ReadLE32(src);
   108     bfReserved1 = SDL_ReadLE16(src);
   109     bfReserved2 = SDL_ReadLE16(src);
   110     bfOffBits = SDL_ReadLE32(src);
   111 
   112     /* Read the Win32 BITMAPINFOHEADER */
   113     biSize = SDL_ReadLE32(src);
   114     if (biSize == 12) {
   115         biWidth = (Uint32) SDL_ReadLE16(src);
   116         biHeight = (Uint32) SDL_ReadLE16(src);
   117         biPlanes = SDL_ReadLE16(src);
   118         biBitCount = SDL_ReadLE16(src);
   119         biCompression = BI_RGB;
   120         biSizeImage = 0;
   121         biXPelsPerMeter = 0;
   122         biYPelsPerMeter = 0;
   123         biClrUsed = 0;
   124         biClrImportant = 0;
   125     } else {
   126         biWidth = SDL_ReadLE32(src);
   127         biHeight = SDL_ReadLE32(src);
   128         biPlanes = SDL_ReadLE16(src);
   129         biBitCount = SDL_ReadLE16(src);
   130         biCompression = SDL_ReadLE32(src);
   131         biSizeImage = SDL_ReadLE32(src);
   132         biXPelsPerMeter = SDL_ReadLE32(src);
   133         biYPelsPerMeter = SDL_ReadLE32(src);
   134         biClrUsed = SDL_ReadLE32(src);
   135         biClrImportant = SDL_ReadLE32(src);
   136     }
   137     if (biHeight < 0) {
   138         topDown = SDL_TRUE;
   139         biHeight = -biHeight;
   140     } else {
   141         topDown = SDL_FALSE;
   142     }
   143 
   144     /* Check for read error */
   145     if (SDL_strcmp(SDL_GetError(), "") != 0) {
   146         was_error = SDL_TRUE;
   147         goto done;
   148     }
   149 
   150     /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   151     switch (biBitCount) {
   152     case 1:
   153     case 4:
   154         ExpandBMP = biBitCount;
   155         biBitCount = 8;
   156         break;
   157     default:
   158         ExpandBMP = 0;
   159         break;
   160     }
   161 
   162     /* We don't support any BMP compression right now */
   163     Rmask = Gmask = Bmask = Amask = 0;
   164     switch (biCompression) {
   165     case BI_RGB:
   166         /* If there are no masks, use the defaults */
   167         if (bfOffBits == (14 + biSize)) {
   168             /* Default values for the BMP format */
   169             switch (biBitCount) {
   170             case 15:
   171             case 16:
   172                 Rmask = 0x7C00;
   173                 Gmask = 0x03E0;
   174                 Bmask = 0x001F;
   175                 break;
   176             case 24:
   177 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   178                 Rmask = 0x000000FF;
   179                 Gmask = 0x0000FF00;
   180                 Bmask = 0x00FF0000;
   181 #else
   182                 Rmask = 0x00FF0000;
   183                 Gmask = 0x0000FF00;
   184                 Bmask = 0x000000FF;
   185 #endif
   186                 break;
   187             case 32:
   188                 Amask = 0xFF000000;
   189                 Rmask = 0x00FF0000;
   190                 Gmask = 0x0000FF00;
   191                 Bmask = 0x000000FF;
   192                 break;
   193             default:
   194                 break;
   195             }
   196             break;
   197         }
   198         /* Fall through -- read the RGB masks */
   199 
   200     case BI_BITFIELDS:
   201         switch (biBitCount) {
   202         case 15:
   203         case 16:
   204             Rmask = SDL_ReadLE32(src);
   205             Gmask = SDL_ReadLE32(src);
   206             Bmask = SDL_ReadLE32(src);
   207             break;
   208         case 32:
   209             Rmask = SDL_ReadLE32(src);
   210             Gmask = SDL_ReadLE32(src);
   211             Bmask = SDL_ReadLE32(src);
   212             Amask = SDL_ReadLE32(src);
   213             break;
   214         default:
   215             break;
   216         }
   217         break;
   218     default:
   219         SDL_SetError("Compressed BMP files not supported");
   220         was_error = SDL_TRUE;
   221         goto done;
   222     }
   223 
   224     /* Create a compatible surface, note that the colors are RGB ordered */
   225     surface =
   226         SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
   227                              Bmask, Amask);
   228     if (surface == NULL) {
   229         was_error = SDL_TRUE;
   230         goto done;
   231     }
   232 
   233     /* Load the palette, if any */
   234     palette = (surface->format)->palette;
   235     if (palette) {
   236         if (biClrUsed == 0) {
   237             biClrUsed = 1 << biBitCount;
   238         }
   239         if ((int) biClrUsed > palette->ncolors) {
   240             palette->ncolors = biClrUsed;
   241             palette->colors =
   242                 (SDL_Color *) SDL_realloc(palette->colors,
   243                                           palette->ncolors *
   244                                           sizeof(*palette->colors));
   245             if (!palette->colors) {
   246                 SDL_OutOfMemory();
   247                 was_error = SDL_TRUE;
   248                 goto done;
   249             }
   250         } else if ((int) biClrUsed < palette->ncolors) {
   251             palette->ncolors = biClrUsed;
   252         }
   253         if (biSize == 12) {
   254             for (i = 0; i < (int) biClrUsed; ++i) {
   255                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   256                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   257                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   258                 palette->colors[i].unused = SDL_ALPHA_OPAQUE;
   259             }
   260         } else {
   261             for (i = 0; i < (int) biClrUsed; ++i) {
   262                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
   263                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
   264                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
   265                 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   266             }
   267         }
   268     }
   269 
   270     /* Read the surface pixels.  Note that the bmp image is upside down */
   271     if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   272         SDL_Error(SDL_EFSEEK);
   273         was_error = SDL_TRUE;
   274         goto done;
   275     }
   276     top = (Uint8 *)surface->pixels;
   277     end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   278     switch (ExpandBMP) {
   279     case 1:
   280         bmpPitch = (biWidth + 7) >> 3;
   281         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   282         break;
   283     case 4:
   284         bmpPitch = (biWidth + 1) >> 1;
   285         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   286         break;
   287     default:
   288         pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
   289         break;
   290     }
   291     if (topDown) {
   292         bits = top;
   293     } else {
   294         bits = end - surface->pitch;
   295     }
   296     while (bits >= top && bits < end) {
   297         switch (ExpandBMP) {
   298         case 1:
   299         case 4:{
   300                 Uint8 pixel = 0;
   301                 int shift = (8 - ExpandBMP);
   302                 for (i = 0; i < surface->w; ++i) {
   303                     if (i % (8 / ExpandBMP) == 0) {
   304                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   305                             SDL_SetError("Error reading from BMP");
   306                             was_error = SDL_TRUE;
   307                             goto done;
   308                         }
   309                     }
   310                     *(bits + i) = (pixel >> shift);
   311                     pixel <<= ExpandBMP;
   312                 }
   313             }
   314             break;
   315 
   316         default:
   317             if (SDL_RWread(src, bits, 1, surface->pitch)
   318                 != surface->pitch) {
   319                 SDL_Error(SDL_EFREAD);
   320                 was_error = SDL_TRUE;
   321                 goto done;
   322             }
   323 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   324             /* Byte-swap the pixels if needed. Note that the 24bpp
   325                case has already been taken care of above. */
   326             switch (biBitCount) {
   327             case 15:
   328             case 16:{
   329                     Uint16 *pix = (Uint16 *) bits;
   330                     for (i = 0; i < surface->w; i++)
   331                         pix[i] = SDL_Swap16(pix[i]);
   332                     break;
   333                 }
   334 
   335             case 32:{
   336                     Uint32 *pix = (Uint32 *) bits;
   337                     for (i = 0; i < surface->w; i++)
   338                         pix[i] = SDL_Swap32(pix[i]);
   339                     break;
   340                 }
   341             }
   342 #endif
   343             break;
   344         }
   345         /* Skip padding bytes, ugh */
   346         if (pad) {
   347             Uint8 padbyte;
   348             for (i = 0; i < pad; ++i) {
   349                 SDL_RWread(src, &padbyte, 1, 1);
   350             }
   351         }
   352         if (topDown) {
   353             bits += surface->pitch;
   354         } else {
   355             bits -= surface->pitch;
   356         }
   357     }
   358   done:
   359     if (was_error) {
   360         if (src) {
   361             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   362         }
   363         if (surface) {
   364             SDL_FreeSurface(surface);
   365         }
   366         surface = NULL;
   367     }
   368     if (freesrc && src) {
   369         SDL_RWclose(src);
   370     }
   371     return (surface);
   372 }
   373 
   374 int
   375 SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
   376 {
   377     long fp_offset;
   378     int i, pad;
   379     SDL_Surface *surface;
   380     Uint8 *bits;
   381 
   382     /* The Win32 BMP file header (14 bytes) */
   383     char magic[2] = { 'B', 'M' };
   384     Uint32 bfSize;
   385     Uint16 bfReserved1;
   386     Uint16 bfReserved2;
   387     Uint32 bfOffBits;
   388 
   389     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   390     Uint32 biSize;
   391     Sint32 biWidth;
   392     Sint32 biHeight;
   393     Uint16 biPlanes;
   394     Uint16 biBitCount;
   395     Uint32 biCompression;
   396     Uint32 biSizeImage;
   397     Sint32 biXPelsPerMeter;
   398     Sint32 biYPelsPerMeter;
   399     Uint32 biClrUsed;
   400     Uint32 biClrImportant;
   401 
   402     /* Make sure we have somewhere to save */
   403     surface = NULL;
   404     if (dst) {
   405         SDL_bool save32bit = SDL_FALSE;
   406 #ifdef SAVE_32BIT_BMP
   407         /* We can save alpha information in a 32-bit BMP */
   408         if (saveme->map->info.flags & SDL_COPY_COLORKEY ||
   409             saveme->format->Amask) {
   410             save32bit = SDL_TRUE;
   411         }
   412 #endif /* SAVE_32BIT_BMP */
   413 
   414         if (saveme->format->palette && !save32bit) {
   415             if (saveme->format->BitsPerPixel == 8) {
   416                 surface = saveme;
   417             } else {
   418                 SDL_SetError("%d bpp BMP files not supported",
   419                              saveme->format->BitsPerPixel);
   420             }
   421         } else if ((saveme->format->BitsPerPixel == 24) &&
   422 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   423                    (saveme->format->Rmask == 0x00FF0000) &&
   424                    (saveme->format->Gmask == 0x0000FF00) &&
   425                    (saveme->format->Bmask == 0x000000FF)
   426 #else
   427                    (saveme->format->Rmask == 0x000000FF) &&
   428                    (saveme->format->Gmask == 0x0000FF00) &&
   429                    (saveme->format->Bmask == 0x00FF0000)
   430 #endif
   431             ) {
   432             surface = saveme;
   433         } else {
   434             SDL_PixelFormat format;
   435 
   436             /* If the surface has a colorkey or alpha channel we'll save a
   437                32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
   438             if (save32bit) {
   439                 SDL_InitFormat(&format, 
   440 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   441                                SDL_PIXELFORMAT_ARGB8888
   442 #else
   443                                SDL_PIXELFORMAT_BGRA8888
   444 #endif
   445                                );
   446             } else {
   447                 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
   448             }
   449             surface = SDL_ConvertSurface(saveme, &format, 0);
   450             if (!surface) {
   451                 SDL_SetError("Couldn't convert image to %d bpp",
   452                              format.BitsPerPixel);
   453             }
   454         }
   455     }
   456 
   457     if (surface && (SDL_LockSurface(surface) == 0)) {
   458         const int bw = surface->w * surface->format->BytesPerPixel;
   459 
   460         /* Set the BMP file header values */
   461         bfSize = 0;             /* We'll write this when we're done */
   462         bfReserved1 = 0;
   463         bfReserved2 = 0;
   464         bfOffBits = 0;          /* We'll write this when we're done */
   465 
   466         /* Write the BMP file header values */
   467         fp_offset = SDL_RWtell(dst);
   468         SDL_ClearError();
   469         SDL_RWwrite(dst, magic, 2, 1);
   470         SDL_WriteLE32(dst, bfSize);
   471         SDL_WriteLE16(dst, bfReserved1);
   472         SDL_WriteLE16(dst, bfReserved2);
   473         SDL_WriteLE32(dst, bfOffBits);
   474 
   475         /* Set the BMP info values */
   476         biSize = 40;
   477         biWidth = surface->w;
   478         biHeight = surface->h;
   479         biPlanes = 1;
   480         biBitCount = surface->format->BitsPerPixel;
   481         biCompression = BI_RGB;
   482         biSizeImage = surface->h * surface->pitch;
   483         biXPelsPerMeter = 0;
   484         biYPelsPerMeter = 0;
   485         if (surface->format->palette) {
   486             biClrUsed = surface->format->palette->ncolors;
   487         } else {
   488             biClrUsed = 0;
   489         }
   490         biClrImportant = 0;
   491 
   492         /* Write the BMP info values */
   493         SDL_WriteLE32(dst, biSize);
   494         SDL_WriteLE32(dst, biWidth);
   495         SDL_WriteLE32(dst, biHeight);
   496         SDL_WriteLE16(dst, biPlanes);
   497         SDL_WriteLE16(dst, biBitCount);
   498         SDL_WriteLE32(dst, biCompression);
   499         SDL_WriteLE32(dst, biSizeImage);
   500         SDL_WriteLE32(dst, biXPelsPerMeter);
   501         SDL_WriteLE32(dst, biYPelsPerMeter);
   502         SDL_WriteLE32(dst, biClrUsed);
   503         SDL_WriteLE32(dst, biClrImportant);
   504 
   505         /* Write the palette (in BGR color order) */
   506         if (surface->format->palette) {
   507             SDL_Color *colors;
   508             int ncolors;
   509 
   510             colors = surface->format->palette->colors;
   511             ncolors = surface->format->palette->ncolors;
   512             for (i = 0; i < ncolors; ++i) {
   513                 SDL_RWwrite(dst, &colors[i].b, 1, 1);
   514                 SDL_RWwrite(dst, &colors[i].g, 1, 1);
   515                 SDL_RWwrite(dst, &colors[i].r, 1, 1);
   516                 SDL_RWwrite(dst, &colors[i].unused, 1, 1);
   517             }
   518         }
   519 
   520         /* Write the bitmap offset */
   521         bfOffBits = SDL_RWtell(dst) - fp_offset;
   522         if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
   523             SDL_Error(SDL_EFSEEK);
   524         }
   525         SDL_WriteLE32(dst, bfOffBits);
   526         if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   527             SDL_Error(SDL_EFSEEK);
   528         }
   529 
   530         /* Write the bitmap image upside down */
   531         bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   532         pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
   533         while (bits > (Uint8 *) surface->pixels) {
   534             bits -= surface->pitch;
   535             if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
   536                 SDL_Error(SDL_EFWRITE);
   537                 break;
   538             }
   539             if (pad) {
   540                 const Uint8 padbyte = 0;
   541                 for (i = 0; i < pad; ++i) {
   542                     SDL_RWwrite(dst, &padbyte, 1, 1);
   543                 }
   544             }
   545         }
   546 
   547         /* Write the BMP file size */
   548         bfSize = SDL_RWtell(dst) - fp_offset;
   549         if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
   550             SDL_Error(SDL_EFSEEK);
   551         }
   552         SDL_WriteLE32(dst, bfSize);
   553         if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
   554             SDL_Error(SDL_EFSEEK);
   555         }
   556 
   557         /* Close it up.. */
   558         SDL_UnlockSurface(surface);
   559         if (surface != saveme) {
   560             SDL_FreeSurface(surface);
   561         }
   562     }
   563 
   564     if (freedst && dst) {
   565         SDL_RWclose(dst);
   566     }
   567     return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
   568 }
   569 
   570 /* vi: set ts=4 sw=4 expandtab: */