src/video/SDL_bmp.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. SDL_LoadBMP_RW
  2. SDL_SaveBMP_RW

   1 /*
   2     SDL - Simple DirectMedia Layer
   3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
   4 
   5     This library is free software; you can redistribute it and/or
   6     modify it under the terms of the GNU Library General Public
   7     License as published by the Free Software Foundation; either
   8     version 2 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     Library General Public License for more details.
  14 
  15     You should have received a copy of the GNU Library General Public
  16     License along with this library; if not, write to the Free
  17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 
  19     Sam Lantinga
  20     slouken@devolution.com
  21 */
  22 
  23 #ifdef SAVE_RCSID
  24 static char rcsid =
  25  "@(#) $Id: SDL_bmp.c,v 1.6.2.5 2001/02/10 07:20:05 hercules Exp $";
  26 #endif
  27 
  28 #ifndef DISABLE_FILE
  29 
  30 /* 
  31    Code to load and save surfaces in Windows BMP format.
  32 
  33    Why support BMP format?  Well, it's a native format for Windows, and
  34    most image processing programs can read and write it.  It would be nice
  35    to be able to have at least one image format that we can natively load
  36    and save, and since PNG is so complex that it would bloat the library,
  37    BMP is a good alternative. 
  38 
  39    This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
  40 */
  41 
  42 #include <string.h>
  43 
  44 #include "SDL_error.h"
  45 #include "SDL_video.h"
  46 #include "SDL_endian.h"
  47 
  48 /* Compression encodings for BMP files */
  49 #ifndef BI_RGB
  50 #define BI_RGB          0
  51 #define BI_RLE8         1
  52 #define BI_RLE4         2
  53 #define BI_BITFIELDS    3
  54 #endif
  55 
  56 
  57 SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
     /* [<][>][^][v][top][bottom][index][help] */
  58 {
  59         int was_error;
  60         long fp_offset;
  61         int bmpPitch;
  62         int i, pad;
  63         SDL_Surface *surface;
  64         Uint32 Rmask;
  65         Uint32 Gmask;
  66         Uint32 Bmask;
  67         SDL_Palette *palette;
  68         Uint8 *bits;
  69         int ExpandBMP;
  70 
  71         /* The Win32 BMP file header (14 bytes) */
  72         char   magic[2];
  73         Uint32 bfSize;
  74         Uint16 bfReserved1;
  75         Uint16 bfReserved2;
  76         Uint32 bfOffBits;
  77 
  78         /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
  79         Uint32 biSize;
  80         Sint32 biWidth;
  81         Sint32 biHeight;
  82         Uint16 biPlanes;
  83         Uint16 biBitCount;
  84         Uint32 biCompression;
  85         Uint32 biSizeImage;
  86         Sint32 biXPelsPerMeter;
  87         Sint32 biYPelsPerMeter;
  88         Uint32 biClrUsed;
  89         Uint32 biClrImportant;
  90 
  91         /* Make sure we are passed a valid data source */
  92         surface = NULL;
  93         was_error = 0;
  94         if ( src == NULL ) {
  95                 was_error = 1;
  96                 goto done;
  97         }
  98 
  99         /* Read in the BMP file header */
 100         fp_offset = SDL_RWtell(src);
 101         SDL_ClearError();
 102         if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
 103                 SDL_Error(SDL_EFREAD);
 104                 was_error = 1;
 105                 goto done;
 106         }
 107         if ( strncmp(magic, "BM", 2) != 0 ) {
 108                 SDL_SetError("File is not a Windows BMP file");
 109                 was_error = 1;
 110                 goto done;
 111         }
 112         bfSize          = SDL_ReadLE32(src);
 113         bfReserved1     = SDL_ReadLE16(src);
 114         bfReserved2     = SDL_ReadLE16(src);
 115         bfOffBits       = SDL_ReadLE32(src);
 116 
 117         /* Read the Win32 BITMAPINFOHEADER */
 118         biSize          = SDL_ReadLE32(src);
 119         if ( biSize == 12 ) {
 120                 biWidth         = (Uint32)SDL_ReadLE16(src);
 121                 biHeight        = (Uint32)SDL_ReadLE16(src);
 122                 biPlanes        = SDL_ReadLE16(src);
 123                 biBitCount      = SDL_ReadLE16(src);
 124                 biCompression   = BI_RGB;
 125                 biSizeImage     = 0;
 126                 biXPelsPerMeter = 0;
 127                 biYPelsPerMeter = 0;
 128                 biClrUsed       = 0;
 129                 biClrImportant  = 0;
 130         } else {
 131                 biWidth         = SDL_ReadLE32(src);
 132                 biHeight        = SDL_ReadLE32(src);
 133                 biPlanes        = SDL_ReadLE16(src);
 134                 biBitCount      = SDL_ReadLE16(src);
 135                 biCompression   = SDL_ReadLE32(src);
 136                 biSizeImage     = SDL_ReadLE32(src);
 137                 biXPelsPerMeter = SDL_ReadLE32(src);
 138                 biYPelsPerMeter = SDL_ReadLE32(src);
 139                 biClrUsed       = SDL_ReadLE32(src);
 140                 biClrImportant  = SDL_ReadLE32(src);
 141         }
 142 
 143         /* Check for read error */
 144         if ( strcmp(SDL_GetError(), "") != 0 ) {
 145                 was_error = 1;
 146                 goto done;
 147         }
 148 
 149         /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
 150         switch (biBitCount) {
 151                 case 1:
 152                 case 4:
 153                         ExpandBMP = biBitCount;
 154                         biBitCount = 8;
 155                         break;
 156                 default:
 157                         ExpandBMP = 0;
 158                         break;
 159         }
 160 
 161         /* We don't support any BMP compression right now */
 162         Rmask = Gmask = Bmask = 0;
 163         switch (biCompression) {
 164                 case BI_RGB:
 165                         /* If there are no masks, use the defaults */
 166                         if ( bfOffBits == (14+biSize) ) {
 167                                 /* Default values for the BMP format */
 168                                 switch (biBitCount) {
 169                                         case 15:
 170                                         case 16:
 171                                                 Rmask = 0x7C00;
 172                                                 Gmask = 0x03E0;
 173                                                 Bmask = 0x001F;
 174                                                 break;
 175                                         case 24:
 176 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
 177                                                 Rmask = 0x000000FF;
 178                                                 Gmask = 0x0000FF00;
 179                                                 Bmask = 0x00FF0000;
 180                                                 break;
 181 #endif
 182                                         case 32:
 183                                                 Rmask = 0x00FF0000;
 184                                                 Gmask = 0x0000FF00;
 185                                                 Bmask = 0x000000FF;
 186                                                 break;
 187                                         default:
 188                                                 break;
 189                                 }
 190                                 break;
 191                         }
 192                         /* Fall through -- read the RGB masks */
 193 
 194                 case BI_BITFIELDS:
 195                         switch (biBitCount) {
 196                                 case 15:
 197                                 case 16:
 198                                 case 32:
 199                                         Rmask = SDL_ReadLE32(src);
 200                                         Gmask = SDL_ReadLE32(src);
 201                                         Bmask = SDL_ReadLE32(src);
 202                                         break;
 203                                 default:
 204                                         break;
 205                         }
 206                         break;
 207                 default:
 208                         SDL_SetError("Compressed BMP files not supported");
 209                         was_error = 1;
 210                         goto done;
 211         }
 212 
 213         /* Create a compatible surface, note that the colors are RGB ordered */
 214         surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
 215                         biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
 216         if ( surface == NULL ) {
 217                 was_error = 1;
 218                 goto done;
 219         }
 220 
 221         /* Load the palette, if any */
 222         palette = (surface->format)->palette;
 223         if ( palette ) {
 224                 if ( biClrUsed == 0 ) {
 225                         biClrUsed = 1 << biBitCount;
 226                 }
 227                 if ( biSize == 12 ) {
 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                                 palette->colors[i].unused = 0;
 233                         }       
 234                 } else {
 235                         for ( i = 0; i < (int)biClrUsed; ++i ) {
 236                                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
 237                                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
 238                                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
 239                                 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
 240                         }       
 241                 }
 242                 palette->ncolors = biClrUsed;
 243         }
 244 
 245         /* Read the surface pixels.  Note that the bmp image is upside down */
 246         if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
 247                 SDL_Error(SDL_EFSEEK);
 248                 was_error = 1;
 249                 goto done;
 250         }
 251         bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
 252         switch (ExpandBMP) {
 253                 case 1:
 254                         bmpPitch = (biWidth + 7) >> 3;
 255                         pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
 256                         break;
 257                 case 4:
 258                         bmpPitch = (biWidth + 1) >> 1;
 259                         pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
 260                         break;
 261                 default:
 262                         pad  = ((surface->pitch%4) ?
 263                                         (4-(surface->pitch%4)) : 0);
 264                         break;
 265         }
 266         while ( bits > (Uint8 *)surface->pixels ) {
 267                 bits -= surface->pitch;
 268                 switch (ExpandBMP) {
 269                         case 1:
 270                         case 4: {
 271                         Uint8 pixel = 0;
 272                         int   shift = (8-ExpandBMP);
 273                         for ( i=0; i<surface->w; ++i ) {
 274                                 if ( i%(8/ExpandBMP) == 0 ) {
 275                                         if ( !SDL_RWread(src, &pixel, 1, 1) ) {
 276                                                 SDL_SetError(
 277                                         "Error reading from BMP");
 278                                                 was_error = 1;
 279                                                 goto done;
 280                                         }
 281                                 }
 282                                 *(bits+i) = (pixel>>shift);
 283                                 pixel <<= ExpandBMP;
 284                         } }
 285                         break;
 286 
 287                         default:
 288                         if ( SDL_RWread(src, bits, 1, surface->pitch)
 289                                                          != surface->pitch ) {
 290                                 SDL_Error(SDL_EFREAD);
 291                                 was_error = 1;
 292                                 goto done;
 293                         }
 294 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
 295                         /* Byte-swap the pixels if needed. Note that the 24bpp
 296                            case has already been taken care of above. */
 297                         switch(biBitCount) {
 298                                 case 15:
 299                                 case 16: {
 300                                         Uint16 *pix = (Uint16 *)bits;
 301                                         for(i = 0; i < surface->w; i++)
 302                                                 pix[i] = SDL_Swap16(pix[i]);
 303                                         break;
 304                                 }
 305 
 306                                 case 32: {
 307                                         Uint32 *pix = (Uint32 *)bits;
 308                                         for(i = 0; i < surface->w; i++)
 309                                                 pix[i] = SDL_Swap32(pix[i]);
 310                                         break;
 311                                 }
 312                         }
 313 #endif
 314                         break;
 315                 }
 316                 /* Skip padding bytes, ugh */
 317                 if ( pad ) {
 318                         Uint8 padbyte;
 319                         for ( i=0; i<pad; ++i ) {
 320                                 SDL_RWread(src, &padbyte, 1, 1);
 321                         }
 322                 }
 323         }
 324 done:
 325         if ( was_error ) {
 326                 if ( surface ) {
 327                         SDL_FreeSurface(surface);
 328                 }
 329                 surface = NULL;
 330         }
 331         if ( freesrc && src ) {
 332                 SDL_RWclose(src);
 333         }
 334         return(surface);
 335 }
 336 
 337 int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
     /* [<][>][^][v][top][bottom][index][help] */
 338 {
 339         long fp_offset;
 340         int i, pad;
 341         SDL_Surface *surface;
 342         Uint8 *bits;
 343 
 344         /* The Win32 BMP file header (14 bytes) */
 345         char   magic[2] = { 'B', 'M' };
 346         Uint32 bfSize;
 347         Uint16 bfReserved1;
 348         Uint16 bfReserved2;
 349         Uint32 bfOffBits;
 350 
 351         /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
 352         Uint32 biSize;
 353         Sint32 biWidth;
 354         Sint32 biHeight;
 355         Uint16 biPlanes;
 356         Uint16 biBitCount;
 357         Uint32 biCompression;
 358         Uint32 biSizeImage;
 359         Sint32 biXPelsPerMeter;
 360         Sint32 biYPelsPerMeter;
 361         Uint32 biClrUsed;
 362         Uint32 biClrImportant;
 363 
 364         /* Make sure we have somewhere to save */
 365         surface = NULL;
 366         if ( dst ) {
 367                 if ( saveme->format->palette ) {
 368                         if ( saveme->format->BitsPerPixel == 8 ) {
 369                                 surface = saveme;
 370                         } else {
 371                                 SDL_SetError("%d bpp BMP files not supported",
 372                                                 saveme->format->BitsPerPixel);
 373                         }
 374                 }
 375                 else if ( (saveme->format->BitsPerPixel == 24) &&
 376 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 377                                 (saveme->format->Rmask == 0x00FF0000) &&
 378                                 (saveme->format->Gmask == 0x0000FF00) &&
 379                                 (saveme->format->Bmask == 0x000000FF)
 380 #else
 381                                 (saveme->format->Rmask == 0x000000FF) &&
 382                                 (saveme->format->Gmask == 0x0000FF00) &&
 383                                 (saveme->format->Bmask == 0x00FF0000)
 384 #endif
 385                           ) {
 386                         surface = saveme;
 387                 } else {
 388                         SDL_Rect bounds;
 389 
 390                         /* Convert to 24 bits per pixel */
 391                         surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
 392                                         saveme->w, saveme->h, 24,
 393 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
 394                                         0x00FF0000, 0x0000FF00, 0x000000FF,
 395 #else
 396                                         0x000000FF, 0x0000FF00, 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,
 405                                                         &bounds) < 0 ) {
 406                                         SDL_FreeSurface(surface);
 407                                         SDL_SetError(
 408                                         "Couldn't convert image to 24 bpp");
 409                                         surface = NULL;
 410                                 }
 411                         }
 412                 }
 413         }
 414 
 415         if ( surface && (SDL_LockSurface(surface) == 0) ) {
 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, SEEK_SET) < 0 ) {
 479                         SDL_Error(SDL_EFSEEK);
 480                 }
 481                 SDL_WriteLE32(dst, bfOffBits);
 482                 if ( SDL_RWseek(dst, fp_offset+bfOffBits, 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  = ((surface->pitch%4) ? (4-(surface->pitch%4)) : 0);
 489                 while ( bits > (Uint8 *)surface->pixels ) {
 490                         bits -= surface->pitch;
 491                         if ( SDL_RWwrite(dst, bits, 1, surface->pitch)
 492                                                         != surface->pitch) {
 493                                 SDL_Error(SDL_EFWRITE);
 494                                 break;
 495                         }
 496                         if ( pad ) {
 497                                 const Uint8 padbyte = 0;
 498                                 for ( i=0; i<pad; ++i ) {
 499                                         SDL_RWwrite(dst, &padbyte, 1, 1);
 500                                 }
 501                         }
 502                 }
 503 
 504                 /* Write the BMP file size */
 505                 bfSize = SDL_RWtell(dst)-fp_offset;
 506                 if ( SDL_RWseek(dst, fp_offset+2, SEEK_SET) < 0 ) {
 507                         SDL_Error(SDL_EFSEEK);
 508                 }
 509                 SDL_WriteLE32(dst, bfSize);
 510                 if ( SDL_RWseek(dst, fp_offset+bfSize, SEEK_SET) < 0 ) {
 511                         SDL_Error(SDL_EFSEEK);
 512                 }
 513 
 514                 /* Close it up.. */
 515                 SDL_UnlockSurface(surface);
 516                 if ( surface != saveme ) {
 517                         SDL_FreeSurface(surface);
 518                 }
 519         }
 520 
 521         if ( freedst && dst ) {
 522                 SDL_RWclose(dst);
 523         }
 524         return((strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
 525 }
 526 
 527 #endif /* ENABLE_FILE */

/* [<][>][^][v][top][bottom][index][help] */