2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
25 Code to load and save surfaces in Windows BMP format.
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.
33 This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
36 #include "SDL_video.h"
37 #include "SDL_endian.h"
39 /* Compression encodings for BMP files */
44 #define BI_BITFIELDS 3
48 SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
64 /* The Win32 BMP file header (14 bytes) */
71 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
79 Sint32 biXPelsPerMeter;
80 Sint32 biYPelsPerMeter;
82 Uint32 biClrImportant;
84 /* Make sure we are passed a valid data source */
86 was_error = SDL_FALSE;
92 /* Read in the BMP file header */
93 fp_offset = SDL_RWtell(src);
95 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
96 SDL_Error(SDL_EFREAD);
100 if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
101 SDL_SetError("File is not a Windows BMP file");
102 was_error = SDL_TRUE;
105 bfSize = SDL_ReadLE32(src);
106 bfReserved1 = SDL_ReadLE16(src);
107 bfReserved2 = SDL_ReadLE16(src);
108 bfOffBits = SDL_ReadLE32(src);
110 /* Read the Win32 BITMAPINFOHEADER */
111 biSize = SDL_ReadLE32(src);
112 if ( biSize == 12 ) {
113 biWidth = (Uint32)SDL_ReadLE16(src);
114 biHeight = (Uint32)SDL_ReadLE16(src);
115 biPlanes = SDL_ReadLE16(src);
116 biBitCount = SDL_ReadLE16(src);
117 biCompression = BI_RGB;
124 biWidth = SDL_ReadLE32(src);
125 biHeight = SDL_ReadLE32(src);
126 biPlanes = SDL_ReadLE16(src);
127 biBitCount = SDL_ReadLE16(src);
128 biCompression = SDL_ReadLE32(src);
129 biSizeImage = SDL_ReadLE32(src);
130 biXPelsPerMeter = SDL_ReadLE32(src);
131 biYPelsPerMeter = SDL_ReadLE32(src);
132 biClrUsed = SDL_ReadLE32(src);
133 biClrImportant = SDL_ReadLE32(src);
137 biHeight = -biHeight;
142 /* Check for read error */
143 if ( SDL_strcmp(SDL_GetError(), "") != 0 ) {
144 was_error = SDL_TRUE;
148 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
149 switch (biBitCount) {
152 ExpandBMP = biBitCount;
160 /* We don't support any BMP compression right now */
161 Rmask = Gmask = Bmask = 0;
162 switch (biCompression) {
164 /* If there are no masks, use the defaults */
165 if ( bfOffBits == (14+biSize) ) {
166 /* Default values for the BMP format */
167 switch (biBitCount) {
175 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
191 /* Fall through -- read the RGB masks */
194 switch (biBitCount) {
198 Rmask = SDL_ReadLE32(src);
199 Gmask = SDL_ReadLE32(src);
200 Bmask = SDL_ReadLE32(src);
207 SDL_SetError("Compressed BMP files not supported");
208 was_error = SDL_TRUE;
212 /* Create a compatible surface, note that the colors are RGB ordered */
213 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
214 biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
215 if ( surface == NULL ) {
216 was_error = SDL_TRUE;
220 /* Load the palette, if any */
221 palette = (surface->format)->palette;
223 if ( biClrUsed == 0 ) {
224 biClrUsed = 1 << biBitCount;
226 if ( biSize == 12 ) {
227 for ( i = 0; i < (int)biClrUsed; ++i ) {
228 SDL_RWread(src, &palette->colors[i].b, 1, 1);
229 SDL_RWread(src, &palette->colors[i].g, 1, 1);
230 SDL_RWread(src, &palette->colors[i].r, 1, 1);
231 palette->colors[i].unused = 0;
234 for ( i = 0; i < (int)biClrUsed; ++i ) {
235 SDL_RWread(src, &palette->colors[i].b, 1, 1);
236 SDL_RWread(src, &palette->colors[i].g, 1, 1);
237 SDL_RWread(src, &palette->colors[i].r, 1, 1);
238 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
241 palette->ncolors = biClrUsed;
244 /* Read the surface pixels. Note that the bmp image is upside down */
245 if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
246 SDL_Error(SDL_EFSEEK);
247 was_error = SDL_TRUE;
250 top = (Uint8 *)surface->pixels;
251 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
254 bmpPitch = (biWidth + 7) >> 3;
255 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
258 bmpPitch = (biWidth + 1) >> 1;
259 pad = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
262 pad = ((surface->pitch%4) ?
263 (4-(surface->pitch%4)) : 0);
269 bits = end - surface->pitch;
271 while ( bits >= top && bits < end ) {
276 int shift = (8-ExpandBMP);
277 for ( i=0; i<surface->w; ++i ) {
278 if ( i%(8/ExpandBMP) == 0 ) {
279 if ( !SDL_RWread(src, &pixel, 1, 1) ) {
281 "Error reading from BMP");
282 was_error = SDL_TRUE;
286 *(bits+i) = (pixel>>shift);
292 if ( SDL_RWread(src, bits, 1, surface->pitch)
293 != surface->pitch ) {
294 SDL_Error(SDL_EFREAD);
295 was_error = SDL_TRUE;
298 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
299 /* Byte-swap the pixels if needed. Note that the 24bpp
300 case has already been taken care of above. */
304 Uint16 *pix = (Uint16 *)bits;
305 for(i = 0; i < surface->w; i++)
306 pix[i] = SDL_Swap16(pix[i]);
311 Uint32 *pix = (Uint32 *)bits;
312 for(i = 0; i < surface->w; i++)
313 pix[i] = SDL_Swap32(pix[i]);
320 /* Skip padding bytes, ugh */
323 for ( i=0; i<pad; ++i ) {
324 SDL_RWread(src, &padbyte, 1, 1);
328 bits += surface->pitch;
330 bits -= surface->pitch;
336 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
339 SDL_FreeSurface(surface);
343 if ( freesrc && src ) {
349 int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
353 SDL_Surface *surface;
356 /* The Win32 BMP file header (14 bytes) */
357 char magic[2] = { 'B', 'M' };
363 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
369 Uint32 biCompression;
371 Sint32 biXPelsPerMeter;
372 Sint32 biYPelsPerMeter;
374 Uint32 biClrImportant;
376 /* Make sure we have somewhere to save */
379 if ( saveme->format->palette ) {
380 if ( saveme->format->BitsPerPixel == 8 ) {
383 SDL_SetError("%d bpp BMP files not supported",
384 saveme->format->BitsPerPixel);
387 else if ( (saveme->format->BitsPerPixel == 24) &&
388 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
389 (saveme->format->Rmask == 0x00FF0000) &&
390 (saveme->format->Gmask == 0x0000FF00) &&
391 (saveme->format->Bmask == 0x000000FF)
393 (saveme->format->Rmask == 0x000000FF) &&
394 (saveme->format->Gmask == 0x0000FF00) &&
395 (saveme->format->Bmask == 0x00FF0000)
402 /* Convert to 24 bits per pixel */
403 surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
404 saveme->w, saveme->h, 24,
405 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
406 0x00FF0000, 0x0000FF00, 0x000000FF,
408 0x000000FF, 0x0000FF00, 0x00FF0000,
411 if ( surface != NULL ) {
414 bounds.w = saveme->w;
415 bounds.h = saveme->h;
416 if ( SDL_LowerBlit(saveme, &bounds, surface,
418 SDL_FreeSurface(surface);
420 "Couldn't convert image to 24 bpp");
427 if ( surface && (SDL_LockSurface(surface) == 0) ) {
428 const int bw = surface->w*surface->format->BytesPerPixel;
430 /* Set the BMP file header values */
431 bfSize = 0; /* We'll write this when we're done */
434 bfOffBits = 0; /* We'll write this when we're done */
436 /* Write the BMP file header values */
437 fp_offset = SDL_RWtell(dst);
439 SDL_RWwrite(dst, magic, 2, 1);
440 SDL_WriteLE32(dst, bfSize);
441 SDL_WriteLE16(dst, bfReserved1);
442 SDL_WriteLE16(dst, bfReserved2);
443 SDL_WriteLE32(dst, bfOffBits);
445 /* Set the BMP info values */
447 biWidth = surface->w;
448 biHeight = surface->h;
450 biBitCount = surface->format->BitsPerPixel;
451 biCompression = BI_RGB;
452 biSizeImage = surface->h*surface->pitch;
455 if ( surface->format->palette ) {
456 biClrUsed = surface->format->palette->ncolors;
462 /* Write the BMP info values */
463 SDL_WriteLE32(dst, biSize);
464 SDL_WriteLE32(dst, biWidth);
465 SDL_WriteLE32(dst, biHeight);
466 SDL_WriteLE16(dst, biPlanes);
467 SDL_WriteLE16(dst, biBitCount);
468 SDL_WriteLE32(dst, biCompression);
469 SDL_WriteLE32(dst, biSizeImage);
470 SDL_WriteLE32(dst, biXPelsPerMeter);
471 SDL_WriteLE32(dst, biYPelsPerMeter);
472 SDL_WriteLE32(dst, biClrUsed);
473 SDL_WriteLE32(dst, biClrImportant);
475 /* Write the palette (in BGR color order) */
476 if ( surface->format->palette ) {
480 colors = surface->format->palette->colors;
481 ncolors = surface->format->palette->ncolors;
482 for ( i=0; i<ncolors; ++i ) {
483 SDL_RWwrite(dst, &colors[i].b, 1, 1);
484 SDL_RWwrite(dst, &colors[i].g, 1, 1);
485 SDL_RWwrite(dst, &colors[i].r, 1, 1);
486 SDL_RWwrite(dst, &colors[i].unused, 1, 1);
490 /* Write the bitmap offset */
491 bfOffBits = SDL_RWtell(dst)-fp_offset;
492 if ( SDL_RWseek(dst, fp_offset+10, RW_SEEK_SET) < 0 ) {
493 SDL_Error(SDL_EFSEEK);
495 SDL_WriteLE32(dst, bfOffBits);
496 if ( SDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
497 SDL_Error(SDL_EFSEEK);
500 /* Write the bitmap image upside down */
501 bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
502 pad = ((bw%4) ? (4-(bw%4)) : 0);
503 while ( bits > (Uint8 *)surface->pixels ) {
504 bits -= surface->pitch;
505 if ( SDL_RWwrite(dst, bits, 1, bw) != bw) {
506 SDL_Error(SDL_EFWRITE);
510 const Uint8 padbyte = 0;
511 for ( i=0; i<pad; ++i ) {
512 SDL_RWwrite(dst, &padbyte, 1, 1);
517 /* Write the BMP file size */
518 bfSize = SDL_RWtell(dst)-fp_offset;
519 if ( SDL_RWseek(dst, fp_offset+2, RW_SEEK_SET) < 0 ) {
520 SDL_Error(SDL_EFSEEK);
522 SDL_WriteLE32(dst, bfSize);
523 if ( SDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0 ) {
524 SDL_Error(SDL_EFSEEK);
528 SDL_UnlockSurface(surface);
529 if ( surface != saveme ) {
530 SDL_FreeSurface(surface);
534 if ( freedst && dst ) {
537 return((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);