src/video/SDL_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1402 d910939febfa
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* 
    25    Code to load and save surfaces in Windows BMP format.
    26 
    27    Why support BMP format?  Well, it's a native format for Windows, and
    28    most image processing programs can read and write it.  It would be nice
    29    to be able to have at least one image format that we can natively load
    30    and save, and since PNG is so complex that it would bloat the library,
    31    BMP is a good alternative. 
    32 
    33    This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
    34 */
    35 
    36 #include "SDL_video.h"
    37 #include "SDL_endian.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     int was_error;
    52     long fp_offset;
    53     int bmpPitch;
    54     int i, pad;
    55     SDL_Surface *surface;
    56     Uint32 Rmask;
    57     Uint32 Gmask;
    58     Uint32 Bmask;
    59     SDL_Palette *palette;
    60     Uint8 *bits;
    61     int ExpandBMP;
    62 
    63     /* The Win32 BMP file header (14 bytes) */
    64     char magic[2];
    65     Uint32 bfSize;
    66     Uint16 bfReserved1;
    67     Uint16 bfReserved2;
    68     Uint32 bfOffBits;
    69 
    70     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
    71     Uint32 biSize;
    72     Sint32 biWidth;
    73     Sint32 biHeight;
    74     Uint16 biPlanes;
    75     Uint16 biBitCount;
    76     Uint32 biCompression;
    77     Uint32 biSizeImage;
    78     Sint32 biXPelsPerMeter;
    79     Sint32 biYPelsPerMeter;
    80     Uint32 biClrUsed;
    81     Uint32 biClrImportant;
    82 
    83     /* Make sure we are passed a valid data source */
    84     surface = NULL;
    85     was_error = 0;
    86     if (src == NULL) {
    87         was_error = 1;
    88         goto done;
    89     }
    90 
    91     /* Read in the BMP file header */
    92     fp_offset = SDL_RWtell (src);
    93     SDL_ClearError ();
    94     if (SDL_RWread (src, magic, 1, 2) != 2) {
    95         SDL_Error (SDL_EFREAD);
    96         was_error = 1;
    97         goto done;
    98     }
    99     if (SDL_strncmp (magic, "BM", 2) != 0) {
   100         SDL_SetError ("File is not a Windows BMP file");
   101         was_error = 1;
   102         goto done;
   103     }
   104     bfSize = SDL_ReadLE32 (src);
   105     bfReserved1 = SDL_ReadLE16 (src);
   106     bfReserved2 = SDL_ReadLE16 (src);
   107     bfOffBits = SDL_ReadLE32 (src);
   108 
   109     /* Read the Win32 BITMAPINFOHEADER */
   110     biSize = SDL_ReadLE32 (src);
   111     if (biSize == 12) {
   112         biWidth = (Uint32) SDL_ReadLE16 (src);
   113         biHeight = (Uint32) SDL_ReadLE16 (src);
   114         biPlanes = SDL_ReadLE16 (src);
   115         biBitCount = SDL_ReadLE16 (src);
   116         biCompression = BI_RGB;
   117         biSizeImage = 0;
   118         biXPelsPerMeter = 0;
   119         biYPelsPerMeter = 0;
   120         biClrUsed = 0;
   121         biClrImportant = 0;
   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 
   135     /* Check for read error */
   136     if (SDL_strcmp (SDL_GetError (), "") != 0) {
   137         was_error = 1;
   138         goto done;
   139     }
   140 
   141     /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   142     switch (biBitCount) {
   143     case 1:
   144     case 4:
   145         ExpandBMP = biBitCount;
   146         biBitCount = 8;
   147         break;
   148     default:
   149         ExpandBMP = 0;
   150         break;
   151     }
   152 
   153     /* We don't support any BMP compression right now */
   154     Rmask = Gmask = Bmask = 0;
   155     switch (biCompression) {
   156     case BI_RGB:
   157         /* If there are no masks, use the defaults */
   158         if (bfOffBits == (14 + biSize)) {
   159             /* Default values for the BMP format */
   160             switch (biBitCount) {
   161             case 15:
   162             case 16:
   163                 Rmask = 0x7C00;
   164                 Gmask = 0x03E0;
   165                 Bmask = 0x001F;
   166                 break;
   167             case 24:
   168 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   169                 Rmask = 0x000000FF;
   170                 Gmask = 0x0000FF00;
   171                 Bmask = 0x00FF0000;
   172                 break;
   173 #endif
   174             case 32:
   175                 Rmask = 0x00FF0000;
   176                 Gmask = 0x0000FF00;
   177                 Bmask = 0x000000FF;
   178                 break;
   179             default:
   180                 break;
   181             }
   182             break;
   183         }
   184         /* Fall through -- read the RGB masks */
   185 
   186     case BI_BITFIELDS:
   187         switch (biBitCount) {
   188         case 15:
   189         case 16:
   190         case 32:
   191             Rmask = SDL_ReadLE32 (src);
   192             Gmask = SDL_ReadLE32 (src);
   193             Bmask = SDL_ReadLE32 (src);
   194             break;
   195         default:
   196             break;
   197         }
   198         break;
   199     default:
   200         SDL_SetError ("Compressed BMP files not supported");
   201         was_error = 1;
   202         goto done;
   203     }
   204 
   205     /* Create a compatible surface, note that the colors are RGB ordered */
   206     surface = SDL_CreateRGBSurface (SDL_SWSURFACE,
   207                                     biWidth, biHeight, biBitCount, Rmask,
   208                                     Gmask, Bmask, 0);
   209     if (surface == NULL) {
   210         was_error = 1;
   211         goto done;
   212     }
   213 
   214     /* Load the palette, if any */
   215     palette = (surface->format)->palette;
   216     if (palette) {
   217         if (biClrUsed == 0) {
   218             biClrUsed = 1 << biBitCount;
   219         }
   220         if (biSize == 12) {
   221             for (i = 0; i < (int) biClrUsed; ++i) {
   222                 SDL_RWread (src, &palette->colors[i].b, 1, 1);
   223                 SDL_RWread (src, &palette->colors[i].g, 1, 1);
   224                 SDL_RWread (src, &palette->colors[i].r, 1, 1);
   225                 palette->colors[i].unused = 0;
   226             }
   227         } else {
   228             for (i = 0; i < (int) biClrUsed; ++i) {
   229                 SDL_RWread (src, &palette->colors[i].b, 1, 1);
   230                 SDL_RWread (src, &palette->colors[i].g, 1, 1);
   231                 SDL_RWread (src, &palette->colors[i].r, 1, 1);
   232                 SDL_RWread (src, &palette->colors[i].unused, 1, 1);
   233             }
   234         }
   235         palette->ncolors = biClrUsed;
   236     }
   237 
   238     /* Read the surface pixels.  Note that the bmp image is upside down */
   239     if (SDL_RWseek (src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   240         SDL_Error (SDL_EFSEEK);
   241         was_error = 1;
   242         goto done;
   243     }
   244     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   245     switch (ExpandBMP) {
   246     case 1:
   247         bmpPitch = (biWidth + 7) >> 3;
   248         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   249         break;
   250     case 4:
   251         bmpPitch = (biWidth + 1) >> 1;
   252         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   253         break;
   254     default:
   255         pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
   256         break;
   257     }
   258     while (bits > (Uint8 *) surface->pixels) {
   259         bits -= surface->pitch;
   260         switch (ExpandBMP) {
   261         case 1:
   262         case 4:
   263             {
   264                 Uint8 pixel = 0;
   265                 int shift = (8 - ExpandBMP);
   266                 for (i = 0; i < surface->w; ++i) {
   267                     if (i % (8 / ExpandBMP) == 0) {
   268                         if (!SDL_RWread (src, &pixel, 1, 1)) {
   269                             SDL_SetError ("Error reading from BMP");
   270                             was_error = 1;
   271                             goto done;
   272                         }
   273                     }
   274                     *(bits + i) = (pixel >> shift);
   275                     pixel <<= ExpandBMP;
   276                 }
   277             }
   278             break;
   279 
   280         default:
   281             if (SDL_RWread (src, bits, 1, surface->pitch)
   282                 != surface->pitch) {
   283                 SDL_Error (SDL_EFREAD);
   284                 was_error = 1;
   285                 goto done;
   286             }
   287 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   288             /* Byte-swap the pixels if needed. Note that the 24bpp
   289                case has already been taken care of above. */
   290             switch (biBitCount) {
   291             case 15:
   292             case 16:
   293                 {
   294                     Uint16 *pix = (Uint16 *) bits;
   295                     for (i = 0; i < surface->w; i++)
   296                         pix[i] = SDL_Swap16 (pix[i]);
   297                     break;
   298                 }
   299 
   300             case 32:
   301                 {
   302                     Uint32 *pix = (Uint32 *) bits;
   303                     for (i = 0; i < surface->w; i++)
   304                         pix[i] = SDL_Swap32 (pix[i]);
   305                     break;
   306                 }
   307             }
   308 #endif
   309             break;
   310         }
   311         /* Skip padding bytes, ugh */
   312         if (pad) {
   313             Uint8 padbyte;
   314             for (i = 0; i < pad; ++i) {
   315                 SDL_RWread (src, &padbyte, 1, 1);
   316             }
   317         }
   318     }
   319   done:
   320     if (was_error) {
   321         if (src) {
   322             SDL_RWseek (src, fp_offset, RW_SEEK_SET);
   323         }
   324         if (surface) {
   325             SDL_FreeSurface (surface);
   326         }
   327         surface = NULL;
   328     }
   329     if (freesrc && src) {
   330         SDL_RWclose (src);
   331     }
   332     return (surface);
   333 }
   334 
   335 int
   336 SDL_SaveBMP_RW (SDL_Surface * saveme, SDL_RWops * dst, int freedst)
   337 {
   338     long fp_offset;
   339     int i, pad;
   340     SDL_Surface *surface;
   341     Uint8 *bits;
   342 
   343     /* The Win32 BMP file header (14 bytes) */
   344     char magic[2] = { 'B', 'M' };
   345     Uint32 bfSize;
   346     Uint16 bfReserved1;
   347     Uint16 bfReserved2;
   348     Uint32 bfOffBits;
   349 
   350     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   351     Uint32 biSize;
   352     Sint32 biWidth;
   353     Sint32 biHeight;
   354     Uint16 biPlanes;
   355     Uint16 biBitCount;
   356     Uint32 biCompression;
   357     Uint32 biSizeImage;
   358     Sint32 biXPelsPerMeter;
   359     Sint32 biYPelsPerMeter;
   360     Uint32 biClrUsed;
   361     Uint32 biClrImportant;
   362 
   363     /* Make sure we have somewhere to save */
   364     surface = NULL;
   365     if (dst) {
   366         if (saveme->format->palette) {
   367             if (saveme->format->BitsPerPixel == 8) {
   368                 surface = saveme;
   369             } else {
   370                 SDL_SetError ("%d bpp BMP files not supported",
   371                               saveme->format->BitsPerPixel);
   372             }
   373         } else if ((saveme->format->BitsPerPixel == 24) &&
   374 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   375                    (saveme->format->Rmask == 0x00FF0000) &&
   376                    (saveme->format->Gmask == 0x0000FF00) &&
   377                    (saveme->format->Bmask == 0x000000FF)
   378 #else
   379                    (saveme->format->Rmask == 0x000000FF) &&
   380                    (saveme->format->Gmask == 0x0000FF00) &&
   381                    (saveme->format->Bmask == 0x00FF0000)
   382 #endif
   383             ) {
   384             surface = saveme;
   385         } else {
   386             SDL_Rect bounds;
   387 
   388             /* Convert to 24 bits per pixel */
   389             surface = SDL_CreateRGBSurface (SDL_SWSURFACE,
   390                                             saveme->w, saveme->h, 24,
   391 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   392                                             0x00FF0000, 0x0000FF00,
   393                                             0x000000FF,
   394 #else
   395                                             0x000000FF, 0x0000FF00,
   396                                             0x00FF0000,
   397 #endif
   398                                             0);
   399             if (surface != NULL) {
   400                 bounds.x = 0;
   401                 bounds.y = 0;
   402                 bounds.w = saveme->w;
   403                 bounds.h = saveme->h;
   404                 if (SDL_LowerBlit (saveme, &bounds, surface, &bounds) < 0) {
   405                     SDL_FreeSurface (surface);
   406                     SDL_SetError ("Couldn't convert image to 24 bpp");
   407                     surface = NULL;
   408                 }
   409             }
   410         }
   411     }
   412 
   413     if (surface && (SDL_LockSurface (surface) == 0)) {
   414         const int bw = surface->w * surface->format->BytesPerPixel;
   415 
   416         /* Set the BMP file header values */
   417         bfSize = 0;             /* We'll write this when we're done */
   418         bfReserved1 = 0;
   419         bfReserved2 = 0;
   420         bfOffBits = 0;          /* We'll write this when we're done */
   421 
   422         /* Write the BMP file header values */
   423         fp_offset = SDL_RWtell (dst);
   424         SDL_ClearError ();
   425         SDL_RWwrite (dst, magic, 2, 1);
   426         SDL_WriteLE32 (dst, bfSize);
   427         SDL_WriteLE16 (dst, bfReserved1);
   428         SDL_WriteLE16 (dst, bfReserved2);
   429         SDL_WriteLE32 (dst, bfOffBits);
   430 
   431         /* Set the BMP info values */
   432         biSize = 40;
   433         biWidth = surface->w;
   434         biHeight = surface->h;
   435         biPlanes = 1;
   436         biBitCount = surface->format->BitsPerPixel;
   437         biCompression = BI_RGB;
   438         biSizeImage = surface->h * surface->pitch;
   439         biXPelsPerMeter = 0;
   440         biYPelsPerMeter = 0;
   441         if (surface->format->palette) {
   442             biClrUsed = surface->format->palette->ncolors;
   443         } else {
   444             biClrUsed = 0;
   445         }
   446         biClrImportant = 0;
   447 
   448         /* Write the BMP info values */
   449         SDL_WriteLE32 (dst, biSize);
   450         SDL_WriteLE32 (dst, biWidth);
   451         SDL_WriteLE32 (dst, biHeight);
   452         SDL_WriteLE16 (dst, biPlanes);
   453         SDL_WriteLE16 (dst, biBitCount);
   454         SDL_WriteLE32 (dst, biCompression);
   455         SDL_WriteLE32 (dst, biSizeImage);
   456         SDL_WriteLE32 (dst, biXPelsPerMeter);
   457         SDL_WriteLE32 (dst, biYPelsPerMeter);
   458         SDL_WriteLE32 (dst, biClrUsed);
   459         SDL_WriteLE32 (dst, biClrImportant);
   460 
   461         /* Write the palette (in BGR color order) */
   462         if (surface->format->palette) {
   463             SDL_Color *colors;
   464             int ncolors;
   465 
   466             colors = surface->format->palette->colors;
   467             ncolors = surface->format->palette->ncolors;
   468             for (i = 0; i < ncolors; ++i) {
   469                 SDL_RWwrite (dst, &colors[i].b, 1, 1);
   470                 SDL_RWwrite (dst, &colors[i].g, 1, 1);
   471                 SDL_RWwrite (dst, &colors[i].r, 1, 1);
   472                 SDL_RWwrite (dst, &colors[i].unused, 1, 1);
   473             }
   474         }
   475 
   476         /* Write the bitmap offset */
   477         bfOffBits = SDL_RWtell (dst) - fp_offset;
   478         if (SDL_RWseek (dst, fp_offset + 10, RW_SEEK_SET) < 0) {
   479             SDL_Error (SDL_EFSEEK);
   480         }
   481         SDL_WriteLE32 (dst, bfOffBits);
   482         if (SDL_RWseek (dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
   483             SDL_Error (SDL_EFSEEK);
   484         }
   485 
   486         /* Write the bitmap image upside down */
   487         bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   488         pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
   489         while (bits > (Uint8 *) surface->pixels) {
   490             bits -= surface->pitch;
   491             if (SDL_RWwrite (dst, bits, 1, bw) != bw) {
   492                 SDL_Error (SDL_EFWRITE);
   493                 break;
   494             }
   495             if (pad) {
   496                 const Uint8 padbyte = 0;
   497                 for (i = 0; i < pad; ++i) {
   498                     SDL_RWwrite (dst, &padbyte, 1, 1);
   499                 }
   500             }
   501         }
   502 
   503         /* Write the BMP file size */
   504         bfSize = SDL_RWtell (dst) - fp_offset;
   505         if (SDL_RWseek (dst, fp_offset + 2, RW_SEEK_SET) < 0) {
   506             SDL_Error (SDL_EFSEEK);
   507         }
   508         SDL_WriteLE32 (dst, bfSize);
   509         if (SDL_RWseek (dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
   510             SDL_Error (SDL_EFSEEK);
   511         }
   512 
   513         /* Close it up.. */
   514         SDL_UnlockSurface (surface);
   515         if (surface != saveme) {
   516             SDL_FreeSurface (surface);
   517         }
   518     }
   519 
   520     if (freedst && dst) {
   521         SDL_RWclose (dst);
   522     }
   523     return ((SDL_strcmp (SDL_GetError (), "") == 0) ? 0 : -1);
   524 }
   525 
   526 /* vi: set ts=4 sw=4 expandtab: */