]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libsdl/contrib/src/video/SDL_bmp.c
update
[l4.git] / l4 / pkg / libsdl / contrib / src / video / SDL_bmp.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 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
48 SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
49 {
50         SDL_bool was_error;
51         long fp_offset = 0;
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
136         /* stop some compiler warnings. */
137         (void) bfSize;
138         (void) bfReserved1;
139         (void) bfReserved2;
140         (void) biPlanes;
141         (void) biSizeImage;
142         (void) biXPelsPerMeter;
143         (void) biYPelsPerMeter;
144         (void) biClrImportant;
145
146         if (biHeight < 0) {
147                 topDown = SDL_TRUE;
148                 biHeight = -biHeight;
149         } else {
150                 topDown = SDL_FALSE;
151         }
152
153         /* Check for read error */
154         if ( SDL_strcmp(SDL_GetError(), "") != 0 ) {
155                 was_error = SDL_TRUE;
156                 goto done;
157         }
158
159         /* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
160         switch (biBitCount) {
161                 case 1:
162                 case 4:
163                         ExpandBMP = biBitCount;
164                         biBitCount = 8;
165                         break;
166                 default:
167                         ExpandBMP = 0;
168                         break;
169         }
170
171         /* We don't support any BMP compression right now */
172         Rmask = Gmask = Bmask = 0;
173         switch (biCompression) {
174                 case BI_RGB:
175                         /* If there are no masks, use the defaults */
176                         if ( bfOffBits == (14+biSize) ) {
177                                 /* Default values for the BMP format */
178                                 switch (biBitCount) {
179                                         case 15:
180                                         case 16:
181                                                 Rmask = 0x7C00;
182                                                 Gmask = 0x03E0;
183                                                 Bmask = 0x001F;
184                                                 break;
185                                         case 24:
186 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
187                                                 Rmask = 0x000000FF;
188                                                 Gmask = 0x0000FF00;
189                                                 Bmask = 0x00FF0000;
190                                                 break;
191 #endif
192                                         case 32:
193                                                 Rmask = 0x00FF0000;
194                                                 Gmask = 0x0000FF00;
195                                                 Bmask = 0x000000FF;
196                                                 break;
197                                         default:
198                                                 break;
199                                 }
200                                 break;
201                         }
202                         /* Fall through -- read the RGB masks */
203
204                 case BI_BITFIELDS:
205                         switch (biBitCount) {
206                                 case 15:
207                                 case 16:
208                                 case 32:
209                                         Rmask = SDL_ReadLE32(src);
210                                         Gmask = SDL_ReadLE32(src);
211                                         Bmask = SDL_ReadLE32(src);
212                                         break;
213                                 default:
214                                         break;
215                         }
216                         break;
217                 default:
218                         SDL_SetError("Compressed BMP files not supported");
219                         was_error = SDL_TRUE;
220                         goto done;
221         }
222
223         /* Create a compatible surface, note that the colors are RGB ordered */
224         surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
225                         biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
226         if ( surface == NULL ) {
227                 was_error = SDL_TRUE;
228                 goto done;
229         }
230
231         /* Load the palette, if any */
232         palette = (surface->format)->palette;
233         if ( palette ) {
234                 if ( biClrUsed == 0 ) {
235                         biClrUsed = 1 << biBitCount;
236                 }
237                 if ( biSize == 12 ) {
238                         for ( i = 0; i < (int)biClrUsed; ++i ) {
239                                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
240                                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
241                                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
242                                 palette->colors[i].unused = 0;
243                         }       
244                 } else {
245                         for ( i = 0; i < (int)biClrUsed; ++i ) {
246                                 SDL_RWread(src, &palette->colors[i].b, 1, 1);
247                                 SDL_RWread(src, &palette->colors[i].g, 1, 1);
248                                 SDL_RWread(src, &palette->colors[i].r, 1, 1);
249                                 SDL_RWread(src, &palette->colors[i].unused, 1, 1);
250                         }       
251                 }
252                 palette->ncolors = biClrUsed;
253         }
254
255         /* Read the surface pixels.  Note that the bmp image is upside down */
256         if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
257                 SDL_Error(SDL_EFSEEK);
258                 was_error = SDL_TRUE;
259                 goto done;
260         }
261         top = (Uint8 *)surface->pixels;
262         end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
263         switch (ExpandBMP) {
264                 case 1:
265                         bmpPitch = (biWidth + 7) >> 3;
266                         pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
267                         break;
268                 case 4:
269                         bmpPitch = (biWidth + 1) >> 1;
270                         pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
271                         break;
272                 default:
273                         pad  = ((surface->pitch%4) ?
274                                         (4-(surface->pitch%4)) : 0);
275                         break;
276         }
277         if ( topDown ) {
278                 bits = top;
279         } else {
280                 bits = end - surface->pitch;
281         }
282         while ( bits >= top && bits < end ) {
283                 switch (ExpandBMP) {
284                         case 1:
285                         case 4: {
286                         Uint8 pixel = 0;
287                         int   shift = (8-ExpandBMP);
288                         for ( i=0; i<surface->w; ++i ) {
289                                 if ( i%(8/ExpandBMP) == 0 ) {
290                                         if ( !SDL_RWread(src, &pixel, 1, 1) ) {
291                                                 SDL_SetError(
292                                         "Error reading from BMP");
293                                                 was_error = SDL_TRUE;
294                                                 goto done;
295                                         }
296                                 }
297                                 *(bits+i) = (pixel>>shift);
298                                 pixel <<= ExpandBMP;
299                         } }
300                         break;
301
302                         default:
303                         if ( SDL_RWread(src, bits, 1, surface->pitch)
304                                                          != surface->pitch ) {
305                                 SDL_Error(SDL_EFREAD);
306                                 was_error = SDL_TRUE;
307                                 goto done;
308                         }
309 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
310                         /* Byte-swap the pixels if needed. Note that the 24bpp
311                            case has already been taken care of above. */
312                         switch(biBitCount) {
313                                 case 15:
314                                 case 16: {
315                                         Uint16 *pix = (Uint16 *)bits;
316                                         for(i = 0; i < surface->w; i++)
317                                                 pix[i] = SDL_Swap16(pix[i]);
318                                         break;
319                                 }
320
321                                 case 32: {
322                                         Uint32 *pix = (Uint32 *)bits;
323                                         for(i = 0; i < surface->w; i++)
324                                                 pix[i] = SDL_Swap32(pix[i]);
325                                         break;
326                                 }
327                         }
328 #endif
329                         break;
330                 }
331                 /* Skip padding bytes, ugh */
332                 if ( pad ) {
333                         Uint8 padbyte;
334                         for ( i=0; i<pad; ++i ) {
335                                 SDL_RWread(src, &padbyte, 1, 1);
336                         }
337                 }
338                 if ( topDown ) {
339                         bits += surface->pitch;
340                 } else {
341                         bits -= surface->pitch;
342                 }
343         }
344 done:
345         if ( was_error ) {
346                 if ( src ) {
347                         SDL_RWseek(src, fp_offset, RW_SEEK_SET);
348                 }
349                 if ( surface ) {
350                         SDL_FreeSurface(surface);
351                 }
352                 surface = NULL;
353         }
354         if ( freesrc && src ) {
355                 SDL_RWclose(src);
356         }
357         return(surface);
358 }
359
360 int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
361 {
362         long fp_offset;
363         int i, pad;
364         SDL_Surface *surface;
365         Uint8 *bits;
366
367         /* The Win32 BMP file header (14 bytes) */
368         char   magic[2] = { 'B', 'M' };
369         Uint32 bfSize;
370         Uint16 bfReserved1;
371         Uint16 bfReserved2;
372         Uint32 bfOffBits;
373
374         /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
375         Uint32 biSize;
376         Sint32 biWidth;
377         Sint32 biHeight;
378         Uint16 biPlanes;
379         Uint16 biBitCount;
380         Uint32 biCompression;
381         Uint32 biSizeImage;
382         Sint32 biXPelsPerMeter;
383         Sint32 biYPelsPerMeter;
384         Uint32 biClrUsed;
385         Uint32 biClrImportant;
386
387         /* Make sure we have somewhere to save */
388         surface = NULL;
389         if ( dst ) {
390                 if ( saveme->format->palette ) {
391                         if ( saveme->format->BitsPerPixel == 8 ) {
392                                 surface = saveme;
393                         } else {
394                                 SDL_SetError("%d bpp BMP files not supported",
395                                                 saveme->format->BitsPerPixel);
396                         }
397                 }
398                 else if ( (saveme->format->BitsPerPixel == 24) &&
399 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
400                                 (saveme->format->Rmask == 0x00FF0000) &&
401                                 (saveme->format->Gmask == 0x0000FF00) &&
402                                 (saveme->format->Bmask == 0x000000FF)
403 #else
404                                 (saveme->format->Rmask == 0x000000FF) &&
405                                 (saveme->format->Gmask == 0x0000FF00) &&
406                                 (saveme->format->Bmask == 0x00FF0000)
407 #endif
408                           ) {
409                         surface = saveme;
410                 } else {
411                         SDL_Rect bounds;
412
413                         /* Convert to 24 bits per pixel */
414                         surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
415                                         saveme->w, saveme->h, 24,
416 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
417                                         0x00FF0000, 0x0000FF00, 0x000000FF,
418 #else
419                                         0x000000FF, 0x0000FF00, 0x00FF0000,
420 #endif
421                                         0);
422                         if ( surface != NULL ) {
423                                 bounds.x = 0;
424                                 bounds.y = 0;
425                                 bounds.w = saveme->w;
426                                 bounds.h = saveme->h;
427                                 if ( SDL_LowerBlit(saveme, &bounds, surface,
428                                                         &bounds) < 0 ) {
429                                         SDL_FreeSurface(surface);
430                                         SDL_SetError(
431                                         "Couldn't convert image to 24 bpp");
432                                         surface = NULL;
433                                 }
434                         }
435                 }
436         }
437
438         if ( surface && (SDL_LockSurface(surface) == 0) ) {
439                 const int bw = surface->w*surface->format->BytesPerPixel;
440
441                 /* Set the BMP file header values */
442                 bfSize = 0;              /* We'll write this when we're done */
443                 bfReserved1 = 0;
444                 bfReserved2 = 0;
445                 bfOffBits = 0;          /* We'll write this when we're done */
446
447                 /* Write the BMP file header values */
448                 fp_offset = SDL_RWtell(dst);
449                 SDL_ClearError();
450                 SDL_RWwrite(dst, magic, 2, 1);
451                 SDL_WriteLE32(dst, bfSize);
452                 SDL_WriteLE16(dst, bfReserved1);
453                 SDL_WriteLE16(dst, bfReserved2);
454                 SDL_WriteLE32(dst, bfOffBits);
455
456                 /* Set the BMP info values */
457                 biSize = 40;
458                 biWidth = surface->w;
459                 biHeight = surface->h;
460                 biPlanes = 1;
461                 biBitCount = surface->format->BitsPerPixel;
462                 biCompression = BI_RGB;
463                 biSizeImage = surface->h*surface->pitch;
464                 biXPelsPerMeter = 0;
465                 biYPelsPerMeter = 0;
466                 if ( surface->format->palette ) {
467                         biClrUsed = surface->format->palette->ncolors;
468                 } else {
469                         biClrUsed = 0;
470                 }
471                 biClrImportant = 0;
472
473                 /* Write the BMP info values */
474                 SDL_WriteLE32(dst, biSize);
475                 SDL_WriteLE32(dst, biWidth);
476                 SDL_WriteLE32(dst, biHeight);
477                 SDL_WriteLE16(dst, biPlanes);
478                 SDL_WriteLE16(dst, biBitCount);
479                 SDL_WriteLE32(dst, biCompression);
480                 SDL_WriteLE32(dst, biSizeImage);
481                 SDL_WriteLE32(dst, biXPelsPerMeter);
482                 SDL_WriteLE32(dst, biYPelsPerMeter);
483                 SDL_WriteLE32(dst, biClrUsed);
484                 SDL_WriteLE32(dst, biClrImportant);
485
486                 /* Write the palette (in BGR color order) */
487                 if ( surface->format->palette ) {
488                         SDL_Color *colors;
489                         int       ncolors;
490
491                         colors = surface->format->palette->colors;
492                         ncolors = surface->format->palette->ncolors;
493                         for ( i=0; i<ncolors; ++i ) {
494                                 SDL_RWwrite(dst, &colors[i].b, 1, 1);
495                                 SDL_RWwrite(dst, &colors[i].g, 1, 1);
496                                 SDL_RWwrite(dst, &colors[i].r, 1, 1);
497                                 SDL_RWwrite(dst, &colors[i].unused, 1, 1);
498                         }
499                 }
500
501                 /* Write the bitmap offset */
502                 bfOffBits = SDL_RWtell(dst)-fp_offset;
503                 if ( SDL_RWseek(dst, fp_offset+10, RW_SEEK_SET) < 0 ) {
504                         SDL_Error(SDL_EFSEEK);
505                 }
506                 SDL_WriteLE32(dst, bfOffBits);
507                 if ( SDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
508                         SDL_Error(SDL_EFSEEK);
509                 }
510
511                 /* Write the bitmap image upside down */
512                 bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
513                 pad  = ((bw%4) ? (4-(bw%4)) : 0);
514                 while ( bits > (Uint8 *)surface->pixels ) {
515                         bits -= surface->pitch;
516                         if ( SDL_RWwrite(dst, bits, 1, bw) != bw) {
517                                 SDL_Error(SDL_EFWRITE);
518                                 break;
519                         }
520                         if ( pad ) {
521                                 const Uint8 padbyte = 0;
522                                 for ( i=0; i<pad; ++i ) {
523                                         SDL_RWwrite(dst, &padbyte, 1, 1);
524                                 }
525                         }
526                 }
527
528                 /* Write the BMP file size */
529                 bfSize = SDL_RWtell(dst)-fp_offset;
530                 if ( SDL_RWseek(dst, fp_offset+2, RW_SEEK_SET) < 0 ) {
531                         SDL_Error(SDL_EFSEEK);
532                 }
533                 SDL_WriteLE32(dst, bfSize);
534                 if ( SDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0 ) {
535                         SDL_Error(SDL_EFSEEK);
536                 }
537
538                 /* Close it up.. */
539                 SDL_UnlockSurface(surface);
540                 if ( surface != saveme ) {
541                         SDL_FreeSurface(surface);
542                 }
543         }
544
545         if ( freedst && dst ) {
546                 SDL_RWclose(dst);
547         }
548         return((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
549 }