1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../SDL_internal.h"
22
23 /*
24 Code to load and save surfaces in Windows BMP format.
25
26 Why support BMP format? Well, it's a native format for Windows, and
27 most image processing programs can read and write it. It would be nice
28 to be able to have at least one image format that we can natively load
29 and save, and since PNG is so complex that it would bloat the library,
30 BMP is a good alternative.
31
32 This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
33 */
34
35 #include "SDL_hints.h"
36 #include "SDL_video.h"
37 #include "SDL_assert.h"
38 #include "SDL_endian.h"
39 #include "SDL_pixels_c.h"
40
41 #define SAVE_32BIT_BMP
42
43 /* Compression encodings for BMP files */
44 #ifndef BI_RGB
45 #define BI_RGB 0
46 #define BI_RLE8 1
47 #define BI_RLE4 2
48 #define BI_BITFIELDS 3
49 #endif
50
51 /* Logical color space values for BMP files */
52 #ifndef LCS_WINDOWS_COLOR_SPACE
53 /* 0x57696E20 == "Win " */
54 #define LCS_WINDOWS_COLOR_SPACE 0x57696E20
55 #endif
56
CorrectAlphaChannel(SDL_Surface * surface)57 static void CorrectAlphaChannel(SDL_Surface *surface)
58 {
59 /* Check to see if there is any alpha channel data */
60 SDL_bool hasAlpha = SDL_FALSE;
61 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
62 int alphaChannelOffset = 0;
63 #else
64 int alphaChannelOffset = 3;
65 #endif
66 Uint8 *alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
67 Uint8 *end = alpha + surface->h * surface->pitch;
68
69 while (alpha < end) {
70 if (*alpha != 0) {
71 hasAlpha = SDL_TRUE;
72 break;
73 }
74 alpha += 4;
75 }
76
77 if (!hasAlpha) {
78 alpha = ((Uint8*)surface->pixels) + alphaChannelOffset;
79 while (alpha < end) {
80 *alpha = SDL_ALPHA_OPAQUE;
81 alpha += 4;
82 }
83 }
84 }
85
86 SDL_Surface *
SDL_LoadBMP_RW(SDL_RWops * src,int freesrc)87 SDL_LoadBMP_RW(SDL_RWops * src, int freesrc)
88 {
89 SDL_bool was_error;
90 Sint64 fp_offset = 0;
91 int bmpPitch;
92 int i, pad;
93 SDL_Surface *surface;
94 Uint32 Rmask = 0;
95 Uint32 Gmask = 0;
96 Uint32 Bmask = 0;
97 Uint32 Amask = 0;
98 SDL_Palette *palette;
99 Uint8 *bits;
100 Uint8 *top, *end;
101 SDL_bool topDown;
102 int ExpandBMP;
103 SDL_bool haveRGBMasks = SDL_FALSE;
104 SDL_bool haveAlphaMask = SDL_FALSE;
105 SDL_bool correctAlpha = SDL_FALSE;
106
107 /* The Win32 BMP file header (14 bytes) */
108 char magic[2];
109 /* Uint32 bfSize = 0; */
110 /* Uint16 bfReserved1 = 0; */
111 /* Uint16 bfReserved2 = 0; */
112 Uint32 bfOffBits = 0;
113
114 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
115 Uint32 biSize = 0;
116 Sint32 biWidth = 0;
117 Sint32 biHeight = 0;
118 /* Uint16 biPlanes = 0; */
119 Uint16 biBitCount = 0;
120 Uint32 biCompression = 0;
121 /* Uint32 biSizeImage = 0; */
122 /* Sint32 biXPelsPerMeter = 0; */
123 /* Sint32 biYPelsPerMeter = 0; */
124 Uint32 biClrUsed = 0;
125 /* Uint32 biClrImportant = 0; */
126
127 /* Make sure we are passed a valid data source */
128 surface = NULL;
129 was_error = SDL_FALSE;
130 if (src == NULL) {
131 was_error = SDL_TRUE;
132 goto done;
133 }
134
135 /* Read in the BMP file header */
136 fp_offset = SDL_RWtell(src);
137 SDL_ClearError();
138 if (SDL_RWread(src, magic, 1, 2) != 2) {
139 SDL_Error(SDL_EFREAD);
140 was_error = SDL_TRUE;
141 goto done;
142 }
143 if (SDL_strncmp(magic, "BM", 2) != 0) {
144 SDL_SetError("File is not a Windows BMP file");
145 was_error = SDL_TRUE;
146 goto done;
147 }
148 /* bfSize = */ SDL_ReadLE32(src);
149 /* bfReserved1 = */ SDL_ReadLE16(src);
150 /* bfReserved2 = */ SDL_ReadLE16(src);
151 bfOffBits = SDL_ReadLE32(src);
152
153 /* Read the Win32 BITMAPINFOHEADER */
154 biSize = SDL_ReadLE32(src);
155 if (biSize == 12) { /* really old BITMAPCOREHEADER */
156 biWidth = (Uint32) SDL_ReadLE16(src);
157 biHeight = (Uint32) SDL_ReadLE16(src);
158 /* biPlanes = */ SDL_ReadLE16(src);
159 biBitCount = SDL_ReadLE16(src);
160 biCompression = BI_RGB;
161 } else if (biSize >= 40) { /* some version of BITMAPINFOHEADER */
162 Uint32 headerSize;
163 biWidth = SDL_ReadLE32(src);
164 biHeight = SDL_ReadLE32(src);
165 /* biPlanes = */ SDL_ReadLE16(src);
166 biBitCount = SDL_ReadLE16(src);
167 biCompression = SDL_ReadLE32(src);
168 /* biSizeImage = */ SDL_ReadLE32(src);
169 /* biXPelsPerMeter = */ SDL_ReadLE32(src);
170 /* biYPelsPerMeter = */ SDL_ReadLE32(src);
171 biClrUsed = SDL_ReadLE32(src);
172 /* biClrImportant = */ SDL_ReadLE32(src);
173
174 /* 64 == BITMAPCOREHEADER2, an incompatible OS/2 2.x extension. Skip this stuff for now. */
175 if (biSize == 64) {
176 /* ignore these extra fields. */
177 if (biCompression == BI_BITFIELDS) {
178 /* this value is actually huffman compression in this variant. */
179 SDL_SetError("Compressed BMP files not supported");
180 was_error = SDL_TRUE;
181 goto done;
182 }
183 } else {
184 /* This is complicated. If compression is BI_BITFIELDS, then
185 we have 3 DWORDS that specify the RGB masks. This is either
186 stored here in an BITMAPV2INFOHEADER (which only differs in
187 that it adds these RGB masks) and biSize >= 52, or we've got
188 these masks stored in the exact same place, but strictly
189 speaking, this is the bmiColors field in BITMAPINFO immediately
190 following the legacy v1 info header, just past biSize. */
191 if (biCompression == BI_BITFIELDS) {
192 haveRGBMasks = SDL_TRUE;
193 Rmask = SDL_ReadLE32(src);
194 Gmask = SDL_ReadLE32(src);
195 Bmask = SDL_ReadLE32(src);
196
197 /* ...v3 adds an alpha mask. */
198 if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
199 haveAlphaMask = SDL_TRUE;
200 Amask = SDL_ReadLE32(src);
201 }
202 } else {
203 /* the mask fields are ignored for v2+ headers if not BI_BITFIELD. */
204 if (biSize >= 52) { /* BITMAPV2INFOHEADER; adds RGB masks */
205 /*Rmask = */ SDL_ReadLE32(src);
206 /*Gmask = */ SDL_ReadLE32(src);
207 /*Bmask = */ SDL_ReadLE32(src);
208 }
209 if (biSize >= 56) { /* BITMAPV3INFOHEADER; adds alpha mask */
210 /*Amask = */ SDL_ReadLE32(src);
211 }
212 }
213
214 /* Insert other fields here; Wikipedia and MSDN say we're up to
215 v5 of this header, but we ignore those for now (they add gamma,
216 color spaces, etc). Ignoring the weird OS/2 2.x format, we
217 currently parse up to v3 correctly (hopefully!). */
218 }
219
220 /* skip any header bytes we didn't handle... */
221 headerSize = (Uint32) (SDL_RWtell(src) - (fp_offset + 14));
222 if (biSize > headerSize) {
223 SDL_RWseek(src, (biSize - headerSize), RW_SEEK_CUR);
224 }
225 }
226 if (biHeight < 0) {
227 topDown = SDL_TRUE;
228 biHeight = -biHeight;
229 } else {
230 topDown = SDL_FALSE;
231 }
232
233 /* Check for read error */
234 if (SDL_strcmp(SDL_GetError(), "") != 0) {
235 was_error = SDL_TRUE;
236 goto done;
237 }
238
239 /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
240 switch (biBitCount) {
241 case 1:
242 case 4:
243 ExpandBMP = biBitCount;
244 biBitCount = 8;
245 break;
246 default:
247 ExpandBMP = 0;
248 break;
249 }
250
251 /* We don't support any BMP compression right now */
252 switch (biCompression) {
253 case BI_RGB:
254 /* If there are no masks, use the defaults */
255 SDL_assert(!haveRGBMasks);
256 SDL_assert(!haveAlphaMask);
257 /* Default values for the BMP format */
258 switch (biBitCount) {
259 case 15:
260 case 16:
261 Rmask = 0x7C00;
262 Gmask = 0x03E0;
263 Bmask = 0x001F;
264 break;
265 case 24:
266 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
267 Rmask = 0x000000FF;
268 Gmask = 0x0000FF00;
269 Bmask = 0x00FF0000;
270 #else
271 Rmask = 0x00FF0000;
272 Gmask = 0x0000FF00;
273 Bmask = 0x000000FF;
274 #endif
275 break;
276 case 32:
277 /* We don't know if this has alpha channel or not */
278 correctAlpha = SDL_TRUE;
279 Amask = 0xFF000000;
280 Rmask = 0x00FF0000;
281 Gmask = 0x0000FF00;
282 Bmask = 0x000000FF;
283 break;
284 default:
285 break;
286 }
287 break;
288
289 case BI_BITFIELDS:
290 break; /* we handled this in the info header. */
291
292 default:
293 SDL_SetError("Compressed BMP files not supported");
294 was_error = SDL_TRUE;
295 goto done;
296 }
297
298 /* Create a compatible surface, note that the colors are RGB ordered */
299 surface =
300 SDL_CreateRGBSurface(0, biWidth, biHeight, biBitCount, Rmask, Gmask,
301 Bmask, Amask);
302 if (surface == NULL) {
303 was_error = SDL_TRUE;
304 goto done;
305 }
306
307 /* Load the palette, if any */
308 palette = (surface->format)->palette;
309 if (palette) {
310 SDL_assert(biBitCount <= 8);
311 if (biClrUsed == 0) {
312 biClrUsed = 1 << biBitCount;
313 }
314 if ((int) biClrUsed > palette->ncolors) {
315 SDL_Color *colors;
316 int ncolors = biClrUsed;
317 colors =
318 (SDL_Color *) SDL_realloc(palette->colors,
319 ncolors *
320 sizeof(*palette->colors));
321 if (!colors) {
322 SDL_OutOfMemory();
323 was_error = SDL_TRUE;
324 goto done;
325 }
326 palette->ncolors = ncolors;
327 palette->colors = colors;
328 } else if ((int) biClrUsed < palette->ncolors) {
329 palette->ncolors = biClrUsed;
330 }
331 if (biSize == 12) {
332 for (i = 0; i < (int) biClrUsed; ++i) {
333 SDL_RWread(src, &palette->colors[i].b, 1, 1);
334 SDL_RWread(src, &palette->colors[i].g, 1, 1);
335 SDL_RWread(src, &palette->colors[i].r, 1, 1);
336 palette->colors[i].a = SDL_ALPHA_OPAQUE;
337 }
338 } else {
339 for (i = 0; i < (int) biClrUsed; ++i) {
340 SDL_RWread(src, &palette->colors[i].b, 1, 1);
341 SDL_RWread(src, &palette->colors[i].g, 1, 1);
342 SDL_RWread(src, &palette->colors[i].r, 1, 1);
343 SDL_RWread(src, &palette->colors[i].a, 1, 1);
344
345 /* According to Microsoft documentation, the fourth element
346 is reserved and must be zero, so we shouldn't treat it as
347 alpha.
348 */
349 palette->colors[i].a = SDL_ALPHA_OPAQUE;
350 }
351 }
352 }
353
354 /* Read the surface pixels. Note that the bmp image is upside down */
355 if (SDL_RWseek(src, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
356 SDL_Error(SDL_EFSEEK);
357 was_error = SDL_TRUE;
358 goto done;
359 }
360 top = (Uint8 *)surface->pixels;
361 end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
362 switch (ExpandBMP) {
363 case 1:
364 bmpPitch = (biWidth + 7) >> 3;
365 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
366 break;
367 case 4:
368 bmpPitch = (biWidth + 1) >> 1;
369 pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
370 break;
371 default:
372 pad = ((surface->pitch % 4) ? (4 - (surface->pitch % 4)) : 0);
373 break;
374 }
375 if (topDown) {
376 bits = top;
377 } else {
378 bits = end - surface->pitch;
379 }
380 while (bits >= top && bits < end) {
381 switch (ExpandBMP) {
382 case 1:
383 case 4:{
384 Uint8 pixel = 0;
385 int shift = (8 - ExpandBMP);
386 for (i = 0; i < surface->w; ++i) {
387 if (i % (8 / ExpandBMP) == 0) {
388 if (!SDL_RWread(src, &pixel, 1, 1)) {
389 SDL_SetError("Error reading from BMP");
390 was_error = SDL_TRUE;
391 goto done;
392 }
393 }
394 *(bits + i) = (pixel >> shift);
395 pixel <<= ExpandBMP;
396 }
397 }
398 break;
399
400 default:
401 if (SDL_RWread(src, bits, 1, surface->pitch)
402 != surface->pitch) {
403 SDL_Error(SDL_EFREAD);
404 was_error = SDL_TRUE;
405 goto done;
406 }
407 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
408 /* Byte-swap the pixels if needed. Note that the 24bpp
409 case has already been taken care of above. */
410 switch (biBitCount) {
411 case 15:
412 case 16:{
413 Uint16 *pix = (Uint16 *) bits;
414 for (i = 0; i < surface->w; i++)
415 pix[i] = SDL_Swap16(pix[i]);
416 break;
417 }
418
419 case 32:{
420 Uint32 *pix = (Uint32 *) bits;
421 for (i = 0; i < surface->w; i++)
422 pix[i] = SDL_Swap32(pix[i]);
423 break;
424 }
425 }
426 #endif
427 break;
428 }
429 /* Skip padding bytes, ugh */
430 if (pad) {
431 Uint8 padbyte;
432 for (i = 0; i < pad; ++i) {
433 SDL_RWread(src, &padbyte, 1, 1);
434 }
435 }
436 if (topDown) {
437 bits += surface->pitch;
438 } else {
439 bits -= surface->pitch;
440 }
441 }
442 if (correctAlpha) {
443 CorrectAlphaChannel(surface);
444 }
445 done:
446 if (was_error) {
447 if (src) {
448 SDL_RWseek(src, fp_offset, RW_SEEK_SET);
449 }
450 SDL_FreeSurface(surface);
451 surface = NULL;
452 }
453 if (freesrc && src) {
454 SDL_RWclose(src);
455 }
456 return (surface);
457 }
458
459 int
SDL_SaveBMP_RW(SDL_Surface * saveme,SDL_RWops * dst,int freedst)460 SDL_SaveBMP_RW(SDL_Surface * saveme, SDL_RWops * dst, int freedst)
461 {
462 Sint64 fp_offset;
463 int i, pad;
464 SDL_Surface *surface;
465 Uint8 *bits;
466 SDL_bool save32bit = SDL_FALSE;
467 SDL_bool saveLegacyBMP = SDL_FALSE;
468
469 /* The Win32 BMP file header (14 bytes) */
470 char magic[2] = { 'B', 'M' };
471 Uint32 bfSize;
472 Uint16 bfReserved1;
473 Uint16 bfReserved2;
474 Uint32 bfOffBits;
475
476 /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
477 Uint32 biSize;
478 Sint32 biWidth;
479 Sint32 biHeight;
480 Uint16 biPlanes;
481 Uint16 biBitCount;
482 Uint32 biCompression;
483 Uint32 biSizeImage;
484 Sint32 biXPelsPerMeter;
485 Sint32 biYPelsPerMeter;
486 Uint32 biClrUsed;
487 Uint32 biClrImportant;
488
489 /* The additional header members from the Win32 BITMAPV4HEADER struct (108 bytes in total) */
490 Uint32 bV4RedMask = 0;
491 Uint32 bV4GreenMask = 0;
492 Uint32 bV4BlueMask = 0;
493 Uint32 bV4AlphaMask = 0;
494 Uint32 bV4CSType = 0;
495 Sint32 bV4Endpoints[3 * 3] = {0};
496 Uint32 bV4GammaRed = 0;
497 Uint32 bV4GammaGreen = 0;
498 Uint32 bV4GammaBlue = 0;
499
500 /* Make sure we have somewhere to save */
501 surface = NULL;
502 if (dst) {
503 #ifdef SAVE_32BIT_BMP
504 /* We can save alpha information in a 32-bit BMP */
505 if (saveme->format->BitsPerPixel >= 8 && (saveme->format->Amask ||
506 saveme->map->info.flags & SDL_COPY_COLORKEY)) {
507 save32bit = SDL_TRUE;
508 }
509 #endif /* SAVE_32BIT_BMP */
510
511 if (saveme->format->palette && !save32bit) {
512 if (saveme->format->BitsPerPixel == 8) {
513 surface = saveme;
514 } else {
515 SDL_SetError("%d bpp BMP files not supported",
516 saveme->format->BitsPerPixel);
517 }
518 } else if ((saveme->format->BitsPerPixel == 24) && !save32bit &&
519 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
520 (saveme->format->Rmask == 0x00FF0000) &&
521 (saveme->format->Gmask == 0x0000FF00) &&
522 (saveme->format->Bmask == 0x000000FF)
523 #else
524 (saveme->format->Rmask == 0x000000FF) &&
525 (saveme->format->Gmask == 0x0000FF00) &&
526 (saveme->format->Bmask == 0x00FF0000)
527 #endif
528 ) {
529 surface = saveme;
530 } else {
531 SDL_PixelFormat format;
532
533 /* If the surface has a colorkey or alpha channel we'll save a
534 32-bit BMP with alpha channel, otherwise save a 24-bit BMP. */
535 if (save32bit) {
536 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGRA32);
537 } else {
538 SDL_InitFormat(&format, SDL_PIXELFORMAT_BGR24);
539 }
540 surface = SDL_ConvertSurface(saveme, &format, 0);
541 if (!surface) {
542 SDL_SetError("Couldn't convert image to %d bpp",
543 format.BitsPerPixel);
544 }
545 }
546 } else {
547 /* Set no error here because it may overwrite a more useful message from
548 SDL_RWFromFile() if SDL_SaveBMP_RW() is called from SDL_SaveBMP(). */
549 return -1;
550 }
551
552 if (save32bit) {
553 saveLegacyBMP = SDL_GetHintBoolean(SDL_HINT_BMP_SAVE_LEGACY_FORMAT, SDL_FALSE);
554 }
555
556 if (surface && (SDL_LockSurface(surface) == 0)) {
557 const int bw = surface->w * surface->format->BytesPerPixel;
558
559 /* Set the BMP file header values */
560 bfSize = 0; /* We'll write this when we're done */
561 bfReserved1 = 0;
562 bfReserved2 = 0;
563 bfOffBits = 0; /* We'll write this when we're done */
564
565 /* Write the BMP file header values */
566 fp_offset = SDL_RWtell(dst);
567 SDL_ClearError();
568 SDL_RWwrite(dst, magic, 2, 1);
569 SDL_WriteLE32(dst, bfSize);
570 SDL_WriteLE16(dst, bfReserved1);
571 SDL_WriteLE16(dst, bfReserved2);
572 SDL_WriteLE32(dst, bfOffBits);
573
574 /* Set the BMP info values */
575 biSize = 40;
576 biWidth = surface->w;
577 biHeight = surface->h;
578 biPlanes = 1;
579 biBitCount = surface->format->BitsPerPixel;
580 biCompression = BI_RGB;
581 biSizeImage = surface->h * surface->pitch;
582 biXPelsPerMeter = 0;
583 biYPelsPerMeter = 0;
584 if (surface->format->palette) {
585 biClrUsed = surface->format->palette->ncolors;
586 } else {
587 biClrUsed = 0;
588 }
589 biClrImportant = 0;
590
591 /* Set the BMP info values for the version 4 header */
592 if (save32bit && !saveLegacyBMP) {
593 biSize = 108;
594 biCompression = BI_BITFIELDS;
595 /* The BMP format is always little endian, these masks stay the same */
596 bV4RedMask = 0x00ff0000;
597 bV4GreenMask = 0x0000ff00;
598 bV4BlueMask = 0x000000ff;
599 bV4AlphaMask = 0xff000000;
600 bV4CSType = LCS_WINDOWS_COLOR_SPACE;
601 bV4GammaRed = 0;
602 bV4GammaGreen = 0;
603 bV4GammaBlue = 0;
604 }
605
606 /* Write the BMP info values */
607 SDL_WriteLE32(dst, biSize);
608 SDL_WriteLE32(dst, biWidth);
609 SDL_WriteLE32(dst, biHeight);
610 SDL_WriteLE16(dst, biPlanes);
611 SDL_WriteLE16(dst, biBitCount);
612 SDL_WriteLE32(dst, biCompression);
613 SDL_WriteLE32(dst, biSizeImage);
614 SDL_WriteLE32(dst, biXPelsPerMeter);
615 SDL_WriteLE32(dst, biYPelsPerMeter);
616 SDL_WriteLE32(dst, biClrUsed);
617 SDL_WriteLE32(dst, biClrImportant);
618
619 /* Write the BMP info values for the version 4 header */
620 if (save32bit && !saveLegacyBMP) {
621 SDL_WriteLE32(dst, bV4RedMask);
622 SDL_WriteLE32(dst, bV4GreenMask);
623 SDL_WriteLE32(dst, bV4BlueMask);
624 SDL_WriteLE32(dst, bV4AlphaMask);
625 SDL_WriteLE32(dst, bV4CSType);
626 for (i = 0; i < 3 * 3; i++) {
627 SDL_WriteLE32(dst, bV4Endpoints[i]);
628 }
629 SDL_WriteLE32(dst, bV4GammaRed);
630 SDL_WriteLE32(dst, bV4GammaGreen);
631 SDL_WriteLE32(dst, bV4GammaBlue);
632 }
633
634 /* Write the palette (in BGR color order) */
635 if (surface->format->palette) {
636 SDL_Color *colors;
637 int ncolors;
638
639 colors = surface->format->palette->colors;
640 ncolors = surface->format->palette->ncolors;
641 for (i = 0; i < ncolors; ++i) {
642 SDL_RWwrite(dst, &colors[i].b, 1, 1);
643 SDL_RWwrite(dst, &colors[i].g, 1, 1);
644 SDL_RWwrite(dst, &colors[i].r, 1, 1);
645 SDL_RWwrite(dst, &colors[i].a, 1, 1);
646 }
647 }
648
649 /* Write the bitmap offset */
650 bfOffBits = (Uint32)(SDL_RWtell(dst) - fp_offset);
651 if (SDL_RWseek(dst, fp_offset + 10, RW_SEEK_SET) < 0) {
652 SDL_Error(SDL_EFSEEK);
653 }
654 SDL_WriteLE32(dst, bfOffBits);
655 if (SDL_RWseek(dst, fp_offset + bfOffBits, RW_SEEK_SET) < 0) {
656 SDL_Error(SDL_EFSEEK);
657 }
658
659 /* Write the bitmap image upside down */
660 bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
661 pad = ((bw % 4) ? (4 - (bw % 4)) : 0);
662 while (bits > (Uint8 *) surface->pixels) {
663 bits -= surface->pitch;
664 if (SDL_RWwrite(dst, bits, 1, bw) != bw) {
665 SDL_Error(SDL_EFWRITE);
666 break;
667 }
668 if (pad) {
669 const Uint8 padbyte = 0;
670 for (i = 0; i < pad; ++i) {
671 SDL_RWwrite(dst, &padbyte, 1, 1);
672 }
673 }
674 }
675
676 /* Write the BMP file size */
677 bfSize = (Uint32)(SDL_RWtell(dst) - fp_offset);
678 if (SDL_RWseek(dst, fp_offset + 2, RW_SEEK_SET) < 0) {
679 SDL_Error(SDL_EFSEEK);
680 }
681 SDL_WriteLE32(dst, bfSize);
682 if (SDL_RWseek(dst, fp_offset + bfSize, RW_SEEK_SET) < 0) {
683 SDL_Error(SDL_EFSEEK);
684 }
685
686 /* Close it up.. */
687 SDL_UnlockSurface(surface);
688 if (surface != saveme) {
689 SDL_FreeSurface(surface);
690 }
691 }
692
693 if (freedst && dst) {
694 SDL_RWclose(dst);
695 }
696 return ((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
697 }
698
699 /* vi: set ts=4 sw=4 expandtab: */
700