2
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
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.
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.
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
25
"@(#) $Id: SDL_bmp.c,v 1.4 2002/03/06 11:23:03 slouken Exp $";
31
Code to load and save surfaces in Windows BMP format.
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.
39
This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
44
#include "SDL_error.h"
45
#include "SDL_video.h"
46
#include "SDL_endian.h"
48
/* Compression encodings for BMP files */
53
#define BI_BITFIELDS 3
57
SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
71
/* The Win32 BMP file header (14 bytes) */
78
/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
86
Sint32 biXPelsPerMeter;
87
Sint32 biYPelsPerMeter;
89
Uint32 biClrImportant;
91
/* Make sure we are passed a valid data source */
99
/* Read in the BMP file header */
100
fp_offset = SDL_RWtell(src);
102
if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
103
SDL_Error(SDL_EFREAD);
107
if ( strncmp(magic, "BM", 2) != 0 ) {
108
SDL_SetError("File is not a Windows BMP file");
112
bfSize = SDL_ReadLE32(src);
113
bfReserved1 = SDL_ReadLE16(src);
114
bfReserved2 = SDL_ReadLE16(src);
115
bfOffBits = SDL_ReadLE32(src);
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;
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);
143
/* Check for read error */
144
if ( strcmp(SDL_GetError(), "") != 0 ) {
149
/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
150
switch (biBitCount) {
153
ExpandBMP = biBitCount;
161
/* We don't support any BMP compression right now */
162
Rmask = Gmask = Bmask = 0;
163
switch (biCompression) {
165
/* If there are no masks, use the defaults */
166
if ( bfOffBits == (14+biSize) ) {
167
/* Default values for the BMP format */
168
switch (biBitCount) {
176
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
192
/* Fall through -- read the RGB masks */
195
switch (biBitCount) {
199
Rmask = SDL_ReadLE32(src);
200
Gmask = SDL_ReadLE32(src);
201
Bmask = SDL_ReadLE32(src);
208
SDL_SetError("Compressed BMP files not supported");
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 ) {
221
/* Load the palette, if any */
222
palette = (surface->format)->palette;
224
if ( biClrUsed == 0 ) {
225
biClrUsed = 1 << biBitCount;
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;
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);
242
palette->ncolors = biClrUsed;
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);
251
bits = (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);
266
while ( bits > (Uint8 *)surface->pixels ) {
267
bits -= surface->pitch;
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) ) {
277
"Error reading from BMP");
282
*(bits+i) = (pixel>>shift);
288
if ( SDL_RWread(src, bits, 1, surface->pitch)
289
!= surface->pitch ) {
290
SDL_Error(SDL_EFREAD);
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. */
300
Uint16 *pix = (Uint16 *)bits;
301
for(i = 0; i < surface->w; i++)
302
pix[i] = SDL_Swap16(pix[i]);
307
Uint32 *pix = (Uint32 *)bits;
308
for(i = 0; i < surface->w; i++)
309
pix[i] = SDL_Swap32(pix[i]);
316
/* Skip padding bytes, ugh */
319
for ( i=0; i<pad; ++i ) {
320
SDL_RWread(src, &padbyte, 1, 1);
327
SDL_FreeSurface(surface);
331
if ( freesrc && src ) {
337
int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
341
SDL_Surface *surface;
344
/* The Win32 BMP file header (14 bytes) */
345
char magic[2] = { 'B', 'M' };
351
/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
357
Uint32 biCompression;
359
Sint32 biXPelsPerMeter;
360
Sint32 biYPelsPerMeter;
362
Uint32 biClrImportant;
364
/* Make sure we have somewhere to save */
367
if ( saveme->format->palette ) {
368
if ( saveme->format->BitsPerPixel == 8 ) {
371
SDL_SetError("%d bpp BMP files not supported",
372
saveme->format->BitsPerPixel);
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)
381
(saveme->format->Rmask == 0x000000FF) &&
382
(saveme->format->Gmask == 0x0000FF00) &&
383
(saveme->format->Bmask == 0x00FF0000)
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,
396
0x000000FF, 0x0000FF00, 0x00FF0000,
399
if ( surface != NULL ) {
402
bounds.w = saveme->w;
403
bounds.h = saveme->h;
404
if ( SDL_LowerBlit(saveme, &bounds, surface,
406
SDL_FreeSurface(surface);
408
"Couldn't convert image to 24 bpp");
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 */
420
bfOffBits = 0; /* We'll write this when we're done */
422
/* Write the BMP file header values */
423
fp_offset = SDL_RWtell(dst);
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);
431
/* Set the BMP info values */
433
biWidth = surface->w;
434
biHeight = surface->h;
436
biBitCount = surface->format->BitsPerPixel;
437
biCompression = BI_RGB;
438
biSizeImage = surface->h*surface->pitch;
441
if ( surface->format->palette ) {
442
biClrUsed = surface->format->palette->ncolors;
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);
461
/* Write the palette (in BGR color order) */
462
if ( surface->format->palette ) {
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);
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);
481
SDL_WriteLE32(dst, bfOffBits);
482
if ( SDL_RWseek(dst, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
483
SDL_Error(SDL_EFSEEK);
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)
493
SDL_Error(SDL_EFWRITE);
497
const Uint8 padbyte = 0;
498
for ( i=0; i<pad; ++i ) {
499
SDL_RWwrite(dst, &padbyte, 1, 1);
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);
509
SDL_WriteLE32(dst, bfSize);
510
if ( SDL_RWseek(dst, fp_offset+bfSize, SEEK_SET) < 0 ) {
511
SDL_Error(SDL_EFSEEK);
515
SDL_UnlockSurface(surface);
516
if ( surface != saveme ) {
517
SDL_FreeSurface(surface);
521
if ( freedst && dst ) {
524
return((strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
527
#endif /* ENABLE_FILE */