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 "LRSDL_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 "LRSDL_video.h"
37 #include "LRSDL_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 
LRSDL_LoadBMP_RW(LRSDL_RWops * src,int freesrc)48 SDL_Surface * LRSDL_LoadBMP_RW (LRSDL_RWops *src, int freesrc)
49 {
50    LRSDL_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    LRSDL_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 = LRSDL_FALSE;
87    if ( src == NULL ) {
88       was_error = LRSDL_TRUE;
89       goto done;
90    }
91 
92    /* Read in the BMP file header */
93    fp_offset = LRSDL_RWtell(src);
94    LRSDL_ClearError();
95    if ( LRSDL_RWread(src, magic, 1, 2) != 2 ) {
96       LRSDL_Error(SDL_EFREAD);
97       was_error = LRSDL_TRUE;
98       goto done;
99    }
100    if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
101       LRSDL_SetError("File is not a Windows BMP file");
102       was_error = LRSDL_TRUE;
103       goto done;
104    }
105    bfSize		= LRSDL_ReadLE32(src);
106    bfReserved1	= LRSDL_ReadLE16(src);
107    bfReserved2	= LRSDL_ReadLE16(src);
108    bfOffBits	= LRSDL_ReadLE32(src);
109 
110    /* Read the Win32 BITMAPINFOHEADER */
111    biSize		= LRSDL_ReadLE32(src);
112    if ( biSize == 12 ) {
113       biWidth		= (Uint32)LRSDL_ReadLE16(src);
114       biHeight	= (Uint32)LRSDL_ReadLE16(src);
115       biPlanes	= LRSDL_ReadLE16(src);
116       biBitCount	= LRSDL_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		= LRSDL_ReadLE32(src);
125       biHeight	= LRSDL_ReadLE32(src);
126       biPlanes	= LRSDL_ReadLE16(src);
127       biBitCount	= LRSDL_ReadLE16(src);
128       biCompression	= LRSDL_ReadLE32(src);
129       biSizeImage	= LRSDL_ReadLE32(src);
130       biXPelsPerMeter	= LRSDL_ReadLE32(src);
131       biYPelsPerMeter	= LRSDL_ReadLE32(src);
132       biClrUsed	= LRSDL_ReadLE32(src);
133       biClrImportant	= LRSDL_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 = LRSDL_TRUE;
148       biHeight = -biHeight;
149    } else {
150       topDown = LRSDL_FALSE;
151    }
152 
153    /* Check for read error */
154    if ( SDL_strcmp(LRSDL_GetError(), "") != 0 ) {
155       was_error = LRSDL_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 #if defined(ABGR1555)
182                   Bmask = 0x7C00;
183                   Gmask = 0x03E0;
184                   Rmask = 0x001F;
185 #else
186                   Rmask = 0x7C00;
187                   Gmask = 0x03E0;
188                   Bmask = 0x001F;
189 #endif
190                   break;
191                case 24:
192 #ifdef MSB_FIRST
193                   Rmask = 0x000000FF;
194                   Gmask = 0x0000FF00;
195                   Bmask = 0x00FF0000;
196                   break;
197 #endif
198                case 32:
199 #if defined(ABGR1555)
200                   Bmask = 0x00FF0000;
201                   Gmask = 0x0000FF00;
202                   Rmask = 0x000000FF;
203 #else
204                   Rmask = 0x00FF0000;
205                   Gmask = 0x0000FF00;
206                   Bmask = 0x000000FF;
207 #endif
208                   break;
209                default:
210                   break;
211             }
212             break;
213          }
214          /* Fall through -- read the RGB masks */
215 
216       case BI_BITFIELDS:
217          switch (biBitCount) {
218             case 15:
219             case 16:
220             case 32:
221                Rmask = LRSDL_ReadLE32(src);
222                Gmask = LRSDL_ReadLE32(src);
223                Bmask = LRSDL_ReadLE32(src);
224                break;
225             default:
226                break;
227          }
228          break;
229       default:
230          LRSDL_SetError("Compressed BMP files not supported");
231          was_error = LRSDL_TRUE;
232          goto done;
233    }
234 
235    /* Create a compatible surface, note that the colors are RGB ordered */
236    surface = LRSDL_CreateRGBSurface(SDL_SWSURFACE,
237          biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
238    if ( surface == NULL ) {
239       was_error = LRSDL_TRUE;
240       goto done;
241    }
242 
243    /* Load the palette, if any */
244    palette = (surface->format)->palette;
245    if ( palette ) {
246       if ( biClrUsed == 0 ) {
247          biClrUsed = 1 << biBitCount;
248       }
249       if ( biSize == 12 ) {
250          for ( i = 0; i < (int)biClrUsed; ++i ) {
251             LRSDL_RWread(src, &palette->colors[i].b, 1, 1);
252             LRSDL_RWread(src, &palette->colors[i].g, 1, 1);
253             LRSDL_RWread(src, &palette->colors[i].r, 1, 1);
254             palette->colors[i].unused = 0;
255          }
256       } else {
257          for ( i = 0; i < (int)biClrUsed; ++i ) {
258             LRSDL_RWread(src, &palette->colors[i].b, 1, 1);
259             LRSDL_RWread(src, &palette->colors[i].g, 1, 1);
260             LRSDL_RWread(src, &palette->colors[i].r, 1, 1);
261             LRSDL_RWread(src, &palette->colors[i].unused, 1, 1);
262          }
263       }
264       palette->ncolors = biClrUsed;
265    }
266 
267    /* Read the surface pixels.  Note that the bmp image is upside down */
268    if ( LRSDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
269       LRSDL_Error(SDL_EFSEEK);
270       was_error = LRSDL_TRUE;
271       goto done;
272    }
273    top = (Uint8 *)surface->pixels;
274    end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
275    switch (ExpandBMP) {
276       case 1:
277          bmpPitch = (biWidth + 7) >> 3;
278          pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
279          break;
280       case 4:
281          bmpPitch = (biWidth + 1) >> 1;
282          pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
283          break;
284       default:
285          pad  = ((surface->pitch%4) ?
286                (4-(surface->pitch%4)) : 0);
287          break;
288    }
289    if ( topDown ) {
290       bits = top;
291    } else {
292       bits = end - surface->pitch;
293    }
294    while ( bits >= top && bits < end ) {
295       switch (ExpandBMP) {
296          case 1:
297          case 4: {
298                     Uint8 pixel = 0;
299                     int   shift = (8-ExpandBMP);
300                     for ( i=0; i<surface->w; ++i ) {
301                        if ( i%(8/ExpandBMP) == 0 ) {
302                           if ( !LRSDL_RWread(src, &pixel, 1, 1) ) {
303                              LRSDL_SetError(
304                                    "Error reading from BMP");
305                              was_error = LRSDL_TRUE;
306                              goto done;
307                           }
308                        }
309                        *(bits+i) = (pixel>>shift);
310                        pixel <<= ExpandBMP;
311                     } }
312                  break;
313 
314          default:
315                  if ( LRSDL_RWread(src, bits, 1, surface->pitch)
316                        != surface->pitch ) {
317                     LRSDL_Error(SDL_EFREAD);
318                     was_error = LRSDL_TRUE;
319                     goto done;
320                  }
321 #ifdef MSB_FIRST
322                  /* Byte-swap the pixels if needed. Note that the 24bpp
323                     case has already been taken care of above. */
324                  switch(biBitCount) {
325                     case 15:
326                     case 16: {
327                                 Uint16 *pix = (Uint16 *)bits;
328                                 for(i = 0; i < surface->w; i++)
329                                    pix[i] = SDL_Swap16(pix[i]);
330                                 break;
331                              }
332 
333                     case 32: {
334                                 Uint32 *pix = (Uint32 *)bits;
335                                 for(i = 0; i < surface->w; i++)
336                                    pix[i] = SDL_Swap32(pix[i]);
337                                 break;
338                              }
339                  }
340 #endif
341                  break;
342       }
343       /* Skip padding bytes, ugh */
344       if ( pad ) {
345          Uint8 padbyte;
346          for ( i=0; i<pad; ++i ) {
347             LRSDL_RWread(src, &padbyte, 1, 1);
348          }
349       }
350       if ( topDown ) {
351          bits += surface->pitch;
352       } else {
353          bits -= surface->pitch;
354       }
355    }
356 done:
357    if ( was_error ) {
358       if ( src ) {
359          LRSDL_RWseek(src, fp_offset, RW_SEEK_SET);
360       }
361       if ( surface ) {
362          LRSDL_FreeSurface(surface);
363       }
364       surface = NULL;
365    }
366    if ( freesrc && src ) {
367       LRSDL_RWclose(src);
368    }
369    return(surface);
370 }
371 
LRSDL_SaveBMP_RW(SDL_Surface * saveme,LRSDL_RWops * dst,int freedst)372 int LRSDL_SaveBMP_RW (SDL_Surface *saveme, LRSDL_RWops *dst, int freedst)
373 {
374    long fp_offset;
375    int i, pad;
376    SDL_Surface *surface;
377    Uint8 *bits;
378 
379    /* The Win32 BMP file header (14 bytes) */
380    char   magic[2] = { 'B', 'M' };
381    Uint32 bfSize;
382    Uint16 bfReserved1;
383    Uint16 bfReserved2;
384    Uint32 bfOffBits;
385 
386    /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
387    Uint32 biSize;
388    Sint32 biWidth;
389    Sint32 biHeight;
390    Uint16 biPlanes;
391    Uint16 biBitCount;
392    Uint32 biCompression;
393    Uint32 biSizeImage;
394    Sint32 biXPelsPerMeter;
395    Sint32 biYPelsPerMeter;
396    Uint32 biClrUsed;
397    Uint32 biClrImportant;
398 
399    /* Make sure we have somewhere to save */
400    surface = NULL;
401    if ( dst ) {
402       if ( saveme->format->palette ) {
403          if ( saveme->format->BitsPerPixel == 8 ) {
404             surface = saveme;
405          } else {
406             LRSDL_SetError("%d bpp BMP files not supported",
407                   saveme->format->BitsPerPixel);
408          }
409       }
410       else if ( (saveme->format->BitsPerPixel == 24) &&
411 #ifdef MSB_FIRST
412             (saveme->format->Rmask == 0x000000FF) &&
413             (saveme->format->Gmask == 0x0000FF00) &&
414             (saveme->format->Bmask == 0x00FF0000)
415 #else
416             (saveme->format->Rmask == 0x00FF0000) &&
417             (saveme->format->Gmask == 0x0000FF00) &&
418             (saveme->format->Bmask == 0x000000FF)
419 #endif
420             ) {
421          surface = saveme;
422       } else {
423          SDL_Rect bounds;
424 
425          /* Convert to 24 bits per pixel */
426          surface = LRSDL_CreateRGBSurface(SDL_SWSURFACE,
427                saveme->w, saveme->h, 24,
428 #ifdef MSB_FIRST
429                0x000000FF, 0x0000FF00, 0x00FF0000,
430 #else
431                0x00FF0000, 0x0000FF00, 0x000000FF,
432 #endif
433                0);
434          if ( surface != NULL ) {
435             bounds.x = 0;
436             bounds.y = 0;
437             bounds.w = saveme->w;
438             bounds.h = saveme->h;
439             if ( LRSDL_LowerBlit(saveme, &bounds, surface,
440                      &bounds) < 0 ) {
441                LRSDL_FreeSurface(surface);
442                LRSDL_SetError(
443                      "Couldn't convert image to 24 bpp");
444                surface = NULL;
445             }
446          }
447       }
448    }
449 
450    if ( surface )
451    {
452       const int bw = surface->w*surface->format->BytesPerPixel;
453 
454       /* Set the BMP file header values */
455       bfSize = 0;		 /* We'll write this when we're done */
456       bfReserved1 = 0;
457       bfReserved2 = 0;
458       bfOffBits = 0;		/* We'll write this when we're done */
459 
460       /* Write the BMP file header values */
461       fp_offset = LRSDL_RWtell(dst);
462       LRSDL_ClearError();
463       LRSDL_RWwrite(dst, magic, 2, 1);
464       LRSDL_WriteLE32(dst, bfSize);
465       LRSDL_WriteLE16(dst, bfReserved1);
466       LRSDL_WriteLE16(dst, bfReserved2);
467       LRSDL_WriteLE32(dst, bfOffBits);
468 
469       /* Set the BMP info values */
470       biSize = 40;
471       biWidth = surface->w;
472       biHeight = surface->h;
473       biPlanes = 1;
474       biBitCount = surface->format->BitsPerPixel;
475       biCompression = BI_RGB;
476       biSizeImage = surface->h*surface->pitch;
477       biXPelsPerMeter = 0;
478       biYPelsPerMeter = 0;
479       if ( surface->format->palette ) {
480          biClrUsed = surface->format->palette->ncolors;
481       } else {
482          biClrUsed = 0;
483       }
484       biClrImportant = 0;
485 
486       /* Write the BMP info values */
487       LRSDL_WriteLE32(dst, biSize);
488       LRSDL_WriteLE32(dst, biWidth);
489       LRSDL_WriteLE32(dst, biHeight);
490       LRSDL_WriteLE16(dst, biPlanes);
491       LRSDL_WriteLE16(dst, biBitCount);
492       LRSDL_WriteLE32(dst, biCompression);
493       LRSDL_WriteLE32(dst, biSizeImage);
494       LRSDL_WriteLE32(dst, biXPelsPerMeter);
495       LRSDL_WriteLE32(dst, biYPelsPerMeter);
496       LRSDL_WriteLE32(dst, biClrUsed);
497       LRSDL_WriteLE32(dst, biClrImportant);
498 
499       /* Write the palette (in BGR color order) */
500       if ( surface->format->palette ) {
501          SDL_Color *colors;
502          int       ncolors;
503 
504          colors = surface->format->palette->colors;
505          ncolors = surface->format->palette->ncolors;
506          for ( i=0; i<ncolors; ++i ) {
507             LRSDL_RWwrite(dst, &colors[i].b, 1, 1);
508             LRSDL_RWwrite(dst, &colors[i].g, 1, 1);
509             LRSDL_RWwrite(dst, &colors[i].r, 1, 1);
510             LRSDL_RWwrite(dst, &colors[i].unused, 1, 1);
511          }
512       }
513 
514       /* Write the bitmap offset */
515       bfOffBits = LRSDL_RWtell(dst)-fp_offset;
516       if ( LRSDL_RWseek(dst, fp_offset+10, RW_SEEK_SET) < 0 ) {
517          LRSDL_Error(SDL_EFSEEK);
518       }
519       LRSDL_WriteLE32(dst, bfOffBits);
520       if ( LRSDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
521          LRSDL_Error(SDL_EFSEEK);
522       }
523 
524       /* Write the bitmap image upside down */
525       bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
526       pad  = ((bw%4) ? (4-(bw%4)) : 0);
527       while ( bits > (Uint8 *)surface->pixels ) {
528          bits -= surface->pitch;
529          if ( LRSDL_RWwrite(dst, bits, 1, bw) != bw) {
530             LRSDL_Error(SDL_EFWRITE);
531             break;
532          }
533          if ( pad ) {
534             const Uint8 padbyte = 0;
535             for ( i=0; i<pad; ++i ) {
536                LRSDL_RWwrite(dst, &padbyte, 1, 1);
537             }
538          }
539       }
540 
541       /* Write the BMP file size */
542       bfSize = LRSDL_RWtell(dst)-fp_offset;
543       if ( LRSDL_RWseek(dst, fp_offset+2, RW_SEEK_SET) < 0 ) {
544          LRSDL_Error(SDL_EFSEEK);
545       }
546       LRSDL_WriteLE32(dst, bfSize);
547       if ( LRSDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0 ) {
548          LRSDL_Error(SDL_EFSEEK);
549       }
550 
551       if ( surface != saveme ) {
552          LRSDL_FreeSurface(surface);
553       }
554    }
555 
556    if ( freedst && dst ) {
557       LRSDL_RWclose(dst);
558    }
559    return((SDL_strcmp(LRSDL_GetError(), "") == 0) ? 0 : -1);
560 }
561