comparison src/video/SDL_bmp.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children e8157fcb3114
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
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$";
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)
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)
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 */