SDL-1.2.14
[sdl_omap.git] / src / video / SDL_bmp.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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
48SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
49{
50 SDL_bool was_error;
51 long fp_offset;
52 int bmpPitch;
53 int i, pad;
54 SDL_Surface *surface;
55 Uint32 Rmask;
56 Uint32 Gmask;
57 Uint32 Bmask;
58 SDL_Palette *palette;
59 Uint8 *bits;
60 Uint8 *top, *end;
61 SDL_bool topDown;
62 int ExpandBMP;
63
64 /* The Win32 BMP file header (14 bytes) */
65 char magic[2];
66 Uint32 bfSize;
67 Uint16 bfReserved1;
68 Uint16 bfReserved2;
69 Uint32 bfOffBits;
70
71 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
72 Uint32 biSize;
73 Sint32 biWidth;
74 Sint32 biHeight;
75 Uint16 biPlanes;
76 Uint16 biBitCount;
77 Uint32 biCompression;
78 Uint32 biSizeImage;
79 Sint32 biXPelsPerMeter;
80 Sint32 biYPelsPerMeter;
81 Uint32 biClrUsed;
82 Uint32 biClrImportant;
83
84 /* Make sure we are passed a valid data source */
85 surface = NULL;
86 was_error = SDL_FALSE;
87 if ( src == NULL ) {
88 was_error = SDL_TRUE;
89 goto done;
90 }
91
92 /* Read in the BMP file header */
93 fp_offset = SDL_RWtell(src);
94 SDL_ClearError();
95 if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
96 SDL_Error(SDL_EFREAD);
97 was_error = SDL_TRUE;
98 goto done;
99 }
100 if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
101 SDL_SetError("File is not a Windows BMP file");
102 was_error = SDL_TRUE;
103 goto done;
104 }
105 bfSize = SDL_ReadLE32(src);
106 bfReserved1 = SDL_ReadLE16(src);
107 bfReserved2 = SDL_ReadLE16(src);
108 bfOffBits = SDL_ReadLE32(src);
109
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;
118 biSizeImage = 0;
119 biXPelsPerMeter = 0;
120 biYPelsPerMeter = 0;
121 biClrUsed = 0;
122 biClrImportant = 0;
123 } else {
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);
134 }
135 if (biHeight < 0) {
136 topDown = SDL_TRUE;
137 biHeight = -biHeight;
138 } else {
139 topDown = SDL_FALSE;
140 }
141
142 /* Check for read error */
143 if ( SDL_strcmp(SDL_GetError(), "") != 0 ) {
144 was_error = SDL_TRUE;
145 goto done;
146 }
147
148 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
149 switch (biBitCount) {
150 case 1:
151 case 4:
152 ExpandBMP = biBitCount;
153 biBitCount = 8;
154 break;
155 default:
156 ExpandBMP = 0;
157 break;
158 }
159
160 /* We don't support any BMP compression right now */
161 Rmask = Gmask = Bmask = 0;
162 switch (biCompression) {
163 case BI_RGB:
164 /* If there are no masks, use the defaults */
165 if ( bfOffBits == (14+biSize) ) {
166 /* Default values for the BMP format */
167 switch (biBitCount) {
168 case 15:
169 case 16:
170 Rmask = 0x7C00;
171 Gmask = 0x03E0;
172 Bmask = 0x001F;
173 break;
174 case 24:
175#if SDL_BYTEORDER == SDL_BIG_ENDIAN
176 Rmask = 0x000000FF;
177 Gmask = 0x0000FF00;
178 Bmask = 0x00FF0000;
179 break;
180#endif
181 case 32:
182 Rmask = 0x00FF0000;
183 Gmask = 0x0000FF00;
184 Bmask = 0x000000FF;
185 break;
186 default:
187 break;
188 }
189 break;
190 }
191 /* Fall through -- read the RGB masks */
192
193 case BI_BITFIELDS:
194 switch (biBitCount) {
195 case 15:
196 case 16:
197 case 32:
198 Rmask = SDL_ReadLE32(src);
199 Gmask = SDL_ReadLE32(src);
200 Bmask = SDL_ReadLE32(src);
201 break;
202 default:
203 break;
204 }
205 break;
206 default:
207 SDL_SetError("Compressed BMP files not supported");
208 was_error = SDL_TRUE;
209 goto done;
210 }
211
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;
217 goto done;
218 }
219
220 /* Load the palette, if any */
221 palette = (surface->format)->palette;
222 if ( palette ) {
223 if ( biClrUsed == 0 ) {
224 biClrUsed = 1 << biBitCount;
225 }
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;
232 }
233 } else {
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);
239 }
240 }
241 palette->ncolors = biClrUsed;
242 }
243
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;
248 goto done;
249 }
250 top = (Uint8 *)surface->pixels;
251 end = (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 if ( topDown ) {
267 bits = top;
268 } else {
269 bits = end - surface->pitch;
270 }
271 while ( bits >= top && bits < end ) {
272 switch (ExpandBMP) {
273 case 1:
274 case 4: {
275 Uint8 pixel = 0;
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) ) {
280 SDL_SetError(
281 "Error reading from BMP");
282 was_error = SDL_TRUE;
283 goto done;
284 }
285 }
286 *(bits+i) = (pixel>>shift);
287 pixel <<= ExpandBMP;
288 } }
289 break;
290
291 default:
292 if ( SDL_RWread(src, bits, 1, surface->pitch)
293 != surface->pitch ) {
294 SDL_Error(SDL_EFREAD);
295 was_error = SDL_TRUE;
296 goto done;
297 }
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. */
301 switch(biBitCount) {
302 case 15:
303 case 16: {
304 Uint16 *pix = (Uint16 *)bits;
305 for(i = 0; i < surface->w; i++)
306 pix[i] = SDL_Swap16(pix[i]);
307 break;
308 }
309
310 case 32: {
311 Uint32 *pix = (Uint32 *)bits;
312 for(i = 0; i < surface->w; i++)
313 pix[i] = SDL_Swap32(pix[i]);
314 break;
315 }
316 }
317#endif
318 break;
319 }
320 /* Skip padding bytes, ugh */
321 if ( pad ) {
322 Uint8 padbyte;
323 for ( i=0; i<pad; ++i ) {
324 SDL_RWread(src, &padbyte, 1, 1);
325 }
326 }
327 if ( topDown ) {
328 bits += surface->pitch;
329 } else {
330 bits -= surface->pitch;
331 }
332 }
333done:
334 if ( was_error ) {
335 if ( src ) {
336 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
337 }
338 if ( surface ) {
339 SDL_FreeSurface(surface);
340 }
341 surface = NULL;
342 }
343 if ( freesrc && src ) {
344 SDL_RWclose(src);
345 }
346 return(surface);
347}
348
349int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
350{
351 long fp_offset;
352 int i, pad;
353 SDL_Surface *surface;
354 Uint8 *bits;
355
356 /* The Win32 BMP file header (14 bytes) */
357 char magic[2] = { 'B', 'M' };
358 Uint32 bfSize;
359 Uint16 bfReserved1;
360 Uint16 bfReserved2;
361 Uint32 bfOffBits;
362
363 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
364 Uint32 biSize;
365 Sint32 biWidth;
366 Sint32 biHeight;
367 Uint16 biPlanes;
368 Uint16 biBitCount;
369 Uint32 biCompression;
370 Uint32 biSizeImage;
371 Sint32 biXPelsPerMeter;
372 Sint32 biYPelsPerMeter;
373 Uint32 biClrUsed;
374 Uint32 biClrImportant;
375
376 /* Make sure we have somewhere to save */
377 surface = NULL;
378 if ( dst ) {
379 if ( saveme->format->palette ) {
380 if ( saveme->format->BitsPerPixel == 8 ) {
381 surface = saveme;
382 } else {
383 SDL_SetError("%d bpp BMP files not supported",
384 saveme->format->BitsPerPixel);
385 }
386 }
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)
392#else
393 (saveme->format->Rmask == 0x000000FF) &&
394 (saveme->format->Gmask == 0x0000FF00) &&
395 (saveme->format->Bmask == 0x00FF0000)
396#endif
397 ) {
398 surface = saveme;
399 } else {
400 SDL_Rect bounds;
401
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,
407#else
408 0x000000FF, 0x0000FF00, 0x00FF0000,
409#endif
410 0);
411 if ( surface != NULL ) {
412 bounds.x = 0;
413 bounds.y = 0;
414 bounds.w = saveme->w;
415 bounds.h = saveme->h;
416 if ( SDL_LowerBlit(saveme, &bounds, surface,
417 &bounds) < 0 ) {
418 SDL_FreeSurface(surface);
419 SDL_SetError(
420 "Couldn't convert image to 24 bpp");
421 surface = NULL;
422 }
423 }
424 }
425 }
426
427 if ( surface && (SDL_LockSurface(surface) == 0) ) {
428 const int bw = surface->w*surface->format->BytesPerPixel;
429
430 /* Set the BMP file header values */
431 bfSize = 0; /* We'll write this when we're done */
432 bfReserved1 = 0;
433 bfReserved2 = 0;
434 bfOffBits = 0; /* We'll write this when we're done */
435
436 /* Write the BMP file header values */
437 fp_offset = SDL_RWtell(dst);
438 SDL_ClearError();
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);
444
445 /* Set the BMP info values */
446 biSize = 40;
447 biWidth = surface->w;
448 biHeight = surface->h;
449 biPlanes = 1;
450 biBitCount = surface->format->BitsPerPixel;
451 biCompression = BI_RGB;
452 biSizeImage = surface->h*surface->pitch;
453 biXPelsPerMeter = 0;
454 biYPelsPerMeter = 0;
455 if ( surface->format->palette ) {
456 biClrUsed = surface->format->palette->ncolors;
457 } else {
458 biClrUsed = 0;
459 }
460 biClrImportant = 0;
461
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);
474
475 /* Write the palette (in BGR color order) */
476 if ( surface->format->palette ) {
477 SDL_Color *colors;
478 int ncolors;
479
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);
487 }
488 }
489
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);
494 }
495 SDL_WriteLE32(dst, bfOffBits);
496 if ( SDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
497 SDL_Error(SDL_EFSEEK);
498 }
499
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);
507 break;
508 }
509 if ( pad ) {
510 const Uint8 padbyte = 0;
511 for ( i=0; i<pad; ++i ) {
512 SDL_RWwrite(dst, &padbyte, 1, 1);
513 }
514 }
515 }
516
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);
521 }
522 SDL_WriteLE32(dst, bfSize);
523 if ( SDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0 ) {
524 SDL_Error(SDL_EFSEEK);
525 }
526
527 /* Close it up.. */
528 SDL_UnlockSurface(surface);
529 if ( surface != saveme ) {
530 SDL_FreeSurface(surface);
531 }
532 }
533
534 if ( freedst && dst ) {
535 SDL_RWclose(dst);
536 }
537 return((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
538}