1 /*
2      PLIB - A Suite of Portable Game Libraries
3      Copyright (C) 1998,2002  Steve Baker
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 Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 
19      For further information visit http://plib.sourceforge.net
20 
21      $Id: ssgLoadBMP.cxx 2109 2006-12-12 15:29:22Z fayjf $
22 */
23 
24 
25 #include "ssgLocal.h"
26 
27 #ifdef SSG_LOAD_BMP_SUPPORTED
28 
29 static FILE          *curr_image_fd ;
30 static char           curr_image_fname [ 512 ] ;
31 static int            isSwapped ;
32 
33 
swab_short(unsigned short * x)34 static void swab_short ( unsigned short *x )
35 {
36   if ( isSwapped )
37     *x = (( *x >>  8 ) & 0x00FF ) |
38          (( *x <<  8 ) & 0xFF00 ) ;
39 }
40 
swab_int(unsigned int * x)41 static void swab_int ( unsigned int *x )
42 {
43   if ( isSwapped )
44     *x = (( *x >> 24 ) & 0x000000FF ) |
45          (( *x >>  8 ) & 0x0000FF00 ) |
46          (( *x <<  8 ) & 0x00FF0000 ) |
47          (( *x << 24 ) & 0xFF000000 ) ;
48 }
49 
50 /*static void swab_int_array ( int *x, int leng )
51 {
52   if ( ! isSwapped )
53     return ;
54 
55   for ( int i = 0 ; i < leng ; i++ )
56   {
57     *x = (( *x >> 24 ) & 0x000000FF ) |
58          (( *x >>  8 ) & 0x0000FF00 ) |
59          (( *x <<  8 ) & 0x00FF0000 ) |
60          (( *x << 24 ) & 0xFF000000 ) ;
61     x++ ;
62   }
63 }*/
64 
65 
readByte()66 static unsigned char readByte ()
67 {
68   unsigned char x ;
69   fread ( & x, sizeof(unsigned char), 1, curr_image_fd ) ;
70   return x ;
71 }
72 
readShort()73 static unsigned short readShort ()
74 {
75   unsigned short x ;
76   fread ( & x, sizeof(unsigned short), 1, curr_image_fd ) ;
77   swab_short ( & x ) ;
78   return x ;
79 }
80 
readInt()81 static unsigned int readInt ()
82 {
83   unsigned int x ;
84   fread ( & x, sizeof(unsigned int), 1, curr_image_fd ) ;
85   swab_int ( & x ) ;
86   return x ;
87 }
88 
89 
90 /*
91   Original source for BMP loader kindly
92   donated by "Sean L. Palmer" <spalmer@pobox.com>
93 */
94 
95 
96 struct BMPHeader
97 {
98   unsigned short  FileType      ;
99   unsigned int    FileSize      ;
100   unsigned short  Reserved1     ;
101   unsigned short  Reserved2     ;
102   unsigned int    OffBits       ;
103   unsigned int    Size          ;
104   unsigned int    Width         ;
105   unsigned int    Height        ;
106   unsigned short  Planes        ;
107   unsigned short  BitCount      ;
108   unsigned int    Compression   ;
109   unsigned int    SizeImage     ;
110   unsigned int    XPelsPerMeter ;
111   unsigned int    YPelsPerMeter ;
112   unsigned int    ClrUsed       ;
113   unsigned int    ClrImportant  ;
114 } ;
115 
116 
117 struct RGBA
118 {
119   unsigned char r,g,b,a ;
120 } ;
121 
122 
ssgLoadBMP(const char * fname,ssgTextureInfo * info)123 bool ssgLoadBMP ( const char *fname, ssgTextureInfo* info )
124 {
125   int w, h, bpp ;
126   int index=0;
127   bool old_format = false;
128   RGBA pal [ 256 ] ;
129 
130   BMPHeader bmphdr ;
131 
132   /* Open file & get size */
133 
134   strcpy ( curr_image_fname, fname ) ;
135 
136   if ( ( curr_image_fd = fopen ( curr_image_fname, "rb" ) ) == NULL )
137   {
138     char *p = strrchr(curr_image_fname,'_');
139     if (p != 0) {
140       *p = '\0';
141       p++;
142       index = atoi (p);
143       old_format = true;
144       if ( ( curr_image_fd = fopen(curr_image_fname, "rb")) == NULL) {
145         perror ( "ssgLoadTexture" ) ;
146         ulSetError( UL_WARNING, "ssgLoadTexture: Failed to load '%s' for reading.", curr_image_fname );
147         return false ;
148       }
149       p--;
150       *p = '_';
151     }
152     else {
153       perror ( "ssgLoadTexture" ) ;
154       ulSetError( UL_WARNING, "ssgLoadTexture: Failed to open '%s' for reading.", curr_image_fname );
155       return false ;
156     }
157   }
158 
159   /*
160     Load the BMP piecemeal to avoid struct packing issues
161   */
162 
163   isSwapped = FALSE ;
164   bmphdr.FileType = readShort () ;
165 
166   if ( bmphdr.FileType == ((int)'B' + ((int)'M'<<8)) )
167     isSwapped = FALSE ;
168   else
169   if ( bmphdr.FileType == ((int)'M' + ((int)'B'<<8)) )
170     isSwapped = TRUE  ;
171   else
172   {
173     ulSetError ( UL_WARNING, "%s: Unrecognised magic number 0x%04x",
174                             curr_image_fname, bmphdr.FileType ) ;
175     return false ;
176   }
177 
178   bmphdr.FileSize      = readInt   () ;
179   bmphdr.Reserved1     = readShort () ;
180   bmphdr.Reserved2     = readShort () ;
181   bmphdr.OffBits       = readInt   () ;
182   bmphdr.Size          = readInt   () ;
183   bmphdr.Width         = readInt   () ;
184   bmphdr.Height        = readInt   () ;
185   bmphdr.Planes        = readShort () ;
186   bmphdr.BitCount      = readShort () ;
187   bmphdr.Compression   = readInt   () ;
188   bmphdr.SizeImage     = readInt   () ;
189   bmphdr.XPelsPerMeter = readInt   () ;
190   bmphdr.YPelsPerMeter = readInt   () ;
191   bmphdr.ClrUsed       = readInt   () ;
192   bmphdr.ClrImportant  = readInt   () ;
193 
194   w   = bmphdr.Width  ;
195   h   = bmphdr.Height ;
196   bpp = bmphdr.Planes * bmphdr.BitCount ;
197 
198   bool top_down = false ;
199   if ( h < 0 )
200   {
201     top_down = true ;
202     h = -1 * h ;
203   }
204 
205 #ifdef PRINT_BMP_HEADER_DEBUG
206   ulSetError ( UL_DEBUG, "Filetype %04x",      bmphdr.FileType      ) ;
207   ulSetError ( UL_DEBUG, "Filesize %08x",      bmphdr.FileSize      ) ;
208   ulSetError ( UL_DEBUG, "R1 %04x",            bmphdr.Reserved1     ) ;
209   ulSetError ( UL_DEBUG, "R2 %04x",            bmphdr.Reserved2     ) ;
210   ulSetError ( UL_DEBUG, "Offbits %08x",       bmphdr.OffBits       ) ;
211   ulSetError ( UL_DEBUG, "Size %08x",          bmphdr.Size          ) ;
212   ulSetError ( UL_DEBUG, "Width %08x",         bmphdr.Width         ) ;
213   ulSetError ( UL_DEBUG, "Height %08x",        bmphdr.Height        ) ;
214   ulSetError ( UL_DEBUG, "Planes %04x",        bmphdr.Planes        ) ;
215   ulSetError ( UL_DEBUG, "Bitcount %04x",      bmphdr.BitCount      ) ;
216   ulSetError ( UL_DEBUG, "Compression %08x",   bmphdr.Compression   ) ;
217   ulSetError ( UL_DEBUG, "SizeImage %08x",     bmphdr.SizeImage     ) ;
218   ulSetError ( UL_DEBUG, "XPelsPerMeter %08x", bmphdr.XPelsPerMeter ) ;
219   ulSetError ( UL_DEBUG, "YPelsPerMeter %08x", bmphdr.YPelsPerMeter ) ;
220   ulSetError ( UL_DEBUG, "ClrUsed %08x",       bmphdr.ClrUsed       ) ;
221   ulSetError ( UL_DEBUG, "ClrImportant %08x",  bmphdr.ClrImportant  ) ;
222 #endif
223 
224   int isMonochrome = TRUE ;
225   int isOpaque     = TRUE ;
226 
227   if ( bpp <= 8 )
228   {
229     for ( int i = 0 ; i < 256 ; i++ )
230     {
231       pal[i].b = readByte () ;
232       pal[i].g = readByte () ;
233       pal[i].r = readByte () ;
234 
235       /* According to BMP specs, this fourth value is not really alpha value
236          but just a filler byte, so it is ignored for now. */
237       pal[i].a = readByte () ;
238       if (old_format == true) {
239         pal[i].a = (i<index)?0:255;
240 
241       }
242 
243       if ( pal[i].r != pal[i].g ||
244            pal[i].g != pal[i].b ) isMonochrome = FALSE ;
245     }
246   }
247 
248   fseek ( curr_image_fd, bmphdr.OffBits, SEEK_SET ) ;
249 
250   bmphdr.SizeImage = w * h * (bpp / 8) ;
251   GLubyte *data = new GLubyte [ bmphdr.SizeImage ] ;
252 
253   /* read (and maybe flip) image */
254   {
255     int row_size = w * (bpp / 8) ;
256     for ( int y = h-1 ; y >= 0 ; y-- )
257     {
258       GLubyte *row_ptr ;
259       if ( top_down )
260       {
261         /* store flipped image */
262         row_ptr = &data [ y * row_size ] ;
263       }
264       else
265       {
266         /* store without flipping */
267         row_ptr = &data [ ( h - ( y + 1 ) ) * row_size ] ;
268       }
269       if ( fread ( row_ptr, 1, row_size, curr_image_fd ) != (unsigned)row_size )
270       {
271         ulSetError ( UL_WARNING, "Premature EOF in '%s'", curr_image_fname ) ;
272         return false ;
273       }
274     }
275   }
276 
277   fclose ( curr_image_fd ) ;
278 
279   GLubyte *image ;
280   int z ;
281 
282   if ( bpp == 8 )
283   {
284     int i ;
285 
286     // check for diffrent alpha values in the bitmap
287     // assume blending if that's the case
288     for ( i = 1 ; i < w * h ; i++ ) {
289       if  (pal[data[i]].a != pal[data[i-1]].a) {
290         isOpaque = FALSE ;
291         break;
292       }
293     }
294 
295     if ( isMonochrome )
296       z = isOpaque ? 1 : 2 ;
297     else
298       z = isOpaque ? 3 : 4 ;
299 
300     image = new GLubyte [ w * h * z ] ;
301 
302     for ( i = 0 ; i < w * h ; i++ )
303       switch ( z )
304       {
305         case 1 : image [ i       ] = pal[data[i]].r ; break ;
306         case 2 : image [ i*2     ] = pal[data[i]].r ;
307                  image [ i*2 + 1 ] = pal[data[i]].a ; break ;
308         case 3 : image [ i*3     ] = pal[data[i]].r ;
309                  image [ i*3 + 1 ] = pal[data[i]].g ;
310                  image [ i*3 + 2 ] = pal[data[i]].b ; break ;
311         case 4 : image [ i*4     ] = pal[data[i]].r ;
312                  image [ i*4 + 1 ] = pal[data[i]].g ;
313                  image [ i*4 + 2 ] = pal[data[i]].b ;
314                  image [ i*4 + 3 ] = pal[data[i]].a ; break ;
315         default : break ;
316       }
317 
318     delete [] data ;
319   }
320   else
321   if ( bpp == 24 )
322   {
323     z = 3 ;
324     image = data ;
325 
326     /* BGR --> RGB */
327 
328     for ( int i = 0 ; i < w * h ; i++ )
329     {
330       GLubyte tmp = image [ 3 * i ] ;
331       image [ 3 * i ] = image [ 3 * i + 2 ];
332       image [ 3 * i + 2 ] = tmp ;
333     }
334   }
335   else
336   if ( bpp == 32 )
337   {
338     z = 4 ;
339     image = data ;
340 
341     /* BGRA --> RGBA */
342 
343     for ( int i = 0 ; i < w * h ; i++ )
344     {
345       GLubyte tmp = image [ 4 * i ] ;
346       image [ 4 * i ] = image [ 4 * i + 2 ];
347       image [ 4 * i + 2 ] = tmp ;
348     }
349   }
350   else
351   {
352     ulSetError ( UL_WARNING, "ssgLoadTexture: Can't load %d bpp BMP textures.", bpp ) ;
353     return false ;
354   }
355 
356   if ( info != NULL )
357   {
358     info -> width = w ;
359     info -> height = h ;
360     info -> depth = z ;
361     info -> alpha = ( isOpaque == FALSE ) ;
362   }
363 
364   return ssgMakeMipMaps ( image, w, h, z ) ;
365 }
366 
367 #else
368 
ssgLoadBMP(const char * fname,ssgTextureInfo * info)369 bool ssgLoadBMP ( const char *fname, ssgTextureInfo* info )
370 {
371   ulSetError ( UL_WARNING,
372     "ssgLoadTexture: '%s' - BMP support not configured", fname ) ;
373   return false ;
374 }
375 
376 #endif
377