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