GLES1RICE: Update from upstream
[mupen64plus-pandora.git] / source / rice_gles / src / liblinux / bmp.c
1 /*
2 //  source code for the ImageLib BMP functions
3 //
4 //  Copyright (C) 2001 M. Scott Heiman
5 //  All Rights Reserved
6 //
7 // You may use the software for any purpose you see fit. You may modify
8 // it, incorporate it in a commercial application, use it for school,
9 // even turn it in as homework. You must keep the Copyright in the
10 // header and source files. This software is not in the "Public Domain".
11 // You may use this software at your own risk. I have made a reasonable
12 // effort to verify that this software works in the manner I expect it to;
13 // however,...
14 //
15 // THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND
16 // WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING
17 // WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A
18 // PARTICULAR PURPOSE. IN NO EVENT SHALL MICHAEL S. HEIMAN BE LIABLE TO
19 // YOU OR ANYONE ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
20 // CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING
21 // WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE,
22 // OR THE CLAIMS OF THIRD PARTIES, WHETHER OR NOT MICHAEL S. HEIMAN HAS
23 // BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
24 // ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
25 // POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
26 */
27
28 #include "BMGDLL.h"
29 #include "BMGUtils.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <setjmp.h>
33
34 #ifndef _WIN32
35 #include <string.h>
36 #endif // _WIN32
37
38 static const unsigned short BMP_ID = 0x4D42;
39
40 /*
41     ReadBMP - reads the image data from a BMP files and stores it in a
42               BMGImageStruct.
43
44     Inputs:
45         filename    - the name of the file to be opened
46
47     Outputs:
48         img         - the BMGImageStruct containing the image data
49
50     Returns:
51         BMGError - if the file could not be read or a resource error occurred
52         BMG_OK   - if the file was read and the data was stored in img
53
54     Limitations:
55         will not read BMP files using BI_RLE8, BI_RLE4, or BI_BITFIELDS
56 */
57 BMGError ReadBMP( const char *filename,
58               struct BMGImageStruct *img )
59 {
60     FILE *file = NULL;
61     int error;
62     BMGError tmp;
63     unsigned char *p, *q; /*, *q_end; */
64 /*    unsigned int cnt; */
65     int i;
66 /*    int EOBMP; */
67
68     BITMAPFILEHEADER bmfh;
69     BITMAPINFOHEADER bmih;
70 /*
71     unsigned int mask[3];
72 */
73
74     unsigned int DIBScanWidth;
75     unsigned int bit_size, rawbit_size;
76     unsigned char *rawbits = NULL;
77
78     SetLastBMGError( BMG_OK );
79
80     if ( img == NULL )
81         { error = (int) errInvalidBMGImage; goto err_jmp; }
82
83
84     file = fopen( filename, "rb" );
85     if  ( file == NULL )
86         { error = (int) errFileOpen; goto err_jmp; }
87
88         /* read the file header */
89     if ( fread( (void *)&bmfh, sizeof(BITMAPFILEHEADER), 1, file ) != 1 )
90         { error = (int) errFileRead; goto err_jmp; }
91
92     /* confirm that this is a BMP file */
93     if ( bmfh.bfType != BMP_ID )
94         { error = (int) errUnsupportedFileFormat; goto err_jmp; }
95
96     /* read the bitmap info header */
97     if ( fread( (void *)&bmih, sizeof(BITMAPINFOHEADER), 1, file ) != 1 )
98         { error = (int) errFileRead; goto err_jmp; }
99
100     /* abort if this is an unsupported format */
101     if ( bmih.biCompression != BI_RGB )
102         { printf("planes: %i  bits: %i  type: %i   ", bmih.biPlanes, bmih.biBitCount, bmih.biCompression); error = (int) errUnsupportedFileFormat; goto err_jmp; }
103
104     img->bits_per_pixel = (unsigned char)bmih.biBitCount;
105     img->width  = bmih.biWidth;
106     img->height = bmih.biHeight;
107     if ( img->bits_per_pixel <= 8 )
108     {
109         img->palette_size = (unsigned short)bmih.biClrUsed;
110         img->bytes_per_palette_entry = 4U;
111     }
112
113     tmp = AllocateBMGImage( img );
114     if ( tmp != BMG_OK )
115         { error = (int) tmp; goto err_jmp; }
116
117     /* read palette if necessary */
118     if ( img->bits_per_pixel <= 8 )
119     {
120         if ( fread( (void *)img->palette, sizeof(RGBQUAD), img->palette_size,
121                 file ) != (unsigned int)img->palette_size )
122         {
123           error = (int) errFileRead;
124           goto err_jmp;
125         }
126     }
127
128     /* dimensions */
129     DIBScanWidth = ( img->bits_per_pixel * img->width + 7 ) / 8;
130     if ( DIBScanWidth %4 )
131         DIBScanWidth += 4 - DIBScanWidth % 4;
132
133     bit_size = img->scan_width * img->height;
134
135     /* allocate memory for the raw bits */
136     if ( bmih.biCompression != BI_RGB )
137         rawbit_size = bmfh.bfSize - bmfh.bfOffBits;
138     else
139         rawbit_size = DIBScanWidth * img->height;
140
141     rawbits = (unsigned char *)calloc( rawbit_size, 1 );
142     if ( rawbits == NULL )
143         { error = (int) errMemoryAllocation; goto err_jmp; }
144
145     if ( fread( (void *)rawbits, sizeof(unsigned char), rawbit_size, file )
146                    != rawbit_size )
147     {
148         error = (int) errFileRead;
149         goto err_jmp;
150     }
151
152     if ( bmih.biCompression == BI_RGB )
153     {
154         p = rawbits;
155         for ( q = img->bits; q < img->bits + bit_size;
156                          q += img->scan_width, p += DIBScanWidth )
157         {
158             memcpy( (void *)q, (void *)p, img->scan_width );
159         }
160     }
161
162     /* swap rows if necessary */
163     if ( bmih.biHeight < 0 )
164     {
165         for ( i = 0; i < (int)(img->height) / 2; i++ )
166         {
167             p = img->bits + i * img->scan_width;
168             q = img->bits + ((img->height) - i - 1 ) * img->scan_width;
169             memcpy( (void *)rawbits, (void *)p, img->scan_width );
170             memcpy( (void *)p, (void *)q, img->scan_width );
171             memcpy( (void *)q, (void *)rawbits, img->scan_width );
172         }
173     }
174
175     fclose( file );
176     free( rawbits );
177     return BMG_OK;
178
179   /* error handler */
180 err_jmp:
181     if ( file != NULL )
182         fclose( file );
183     if ( rawbits != NULL )
184         free( rawbits );
185     FreeBMGImage( img );
186     SetLastBMGError( (BMGError)error );
187     return (BMGError)error;
188
189 }
190
191 /*
192     WriteBMP - writes the contents of an BMGImageStruct to a bmp file.
193
194     Inputs:
195         filename    - the name of the file to be opened
196         img         - the BMGImageStruct containing the image data
197
198     Returns:
199         BMGError - if a write error or a resource error occurred
200         BMG_OK   - if the data was successfilly stored in filename
201
202     Limitations:
203         will not write BMP files using BI_RLE8, BI_RLE4, or BI_BITFIELDS
204 */
205 BMGError WriteBMP( const char *filename,
206                    struct BMGImageStruct img )
207 {
208     FILE * volatile file = NULL;
209     jmp_buf err_jmp;
210     int error;
211
212     unsigned char * volatile bits = NULL;
213     unsigned int DIBScanWidth;
214     unsigned int BitsPerPixel;
215     unsigned int bit_size; /*, new_bit_size; */
216 /*    unsigned int rawbit_size; */
217     unsigned char *p, *q, *r, *t;
218 /*    unsigned int cnt;  */
219     unsigned char * volatile pColor = NULL;
220
221     BITMAPFILEHEADER bmfh;
222     BITMAPINFOHEADER bmih;
223
224     SetLastBMGError( BMG_OK );
225
226     /* error handler */
227     error = setjmp(err_jmp);
228     if (error != 0)
229     {
230         if (file != NULL)
231             fclose(file);
232         if (bits != NULL)
233             free(bits);
234         if (pColor != NULL)
235             free(pColor);
236         SetLastBMGError((BMGError)error);
237         return (BMGError) error;
238     }
239
240     if ( img.bits == NULL )
241         longjmp( err_jmp, (int)errInvalidBMGImage );
242
243     file = fopen( filename, "wb" );
244     if ( file == NULL )
245         longjmp( err_jmp, (int)errFileOpen );
246
247     /* abort if we do not support the data */
248     if ( img.palette != NULL && img.bytes_per_palette_entry < 3 )
249         longjmp( err_jmp, (int)errInvalidBMGImage );
250
251     /* calculate dimensions */
252     BitsPerPixel = img.bits_per_pixel < 32 ? img.bits_per_pixel : 24U;
253     DIBScanWidth = ( BitsPerPixel * img.width + 7 ) / 8;
254     if ( DIBScanWidth % 4 )
255         DIBScanWidth += 4 - DIBScanWidth % 4;
256     bit_size = DIBScanWidth * img.height;
257 /*    rawbit_size = BITScanWidth * img.height; */
258
259     /* allocate memory for bit array - assume that compression will
260     // actually compress the bitmap */
261     bits = (unsigned char *)calloc( bit_size, 1 );
262     if ( bits == NULL )
263         longjmp( err_jmp, (int)errMemoryAllocation );
264
265     /* some initialization */
266     memset( (void *)&bmih, 0, sizeof(BITMAPINFOHEADER) );
267     bmih.biSize = sizeof(BITMAPINFOHEADER);
268     bmih.biWidth = img.width;
269     bmih.biHeight = img.height;
270     bmih.biPlanes = 1;
271     /* 32-bit images will be stored as 24-bit images to save space.  The BMP
272        format does not use the high word and I do not want to store alpha
273        components in an image format that does not recognize it */
274     bmih.biBitCount = BitsPerPixel;
275     bmih.biCompression = BI_RGB; // assumed
276     bmih.biSizeImage = bit_size; // assumed
277     bmih.biClrUsed = img.palette == NULL ? 0 : img.palette_size;
278     bmih.biClrImportant = img.palette == NULL ? 0 : img.palette_size;
279
280     /* if we are not compressed then copy the raw bits to bits */
281     if ( bmih.biCompression == BI_RGB )
282     {
283         p = img.bits;
284         /* simple memcpy's for images containing < 32-bits per pixel */
285         if ( img.bits_per_pixel < 32 )
286         {
287             for ( q = bits; q < bits + bit_size; q += DIBScanWidth,
288                                                  p += img.scan_width )
289             {
290                 memcpy( (void *)q, (void *)p, img.scan_width );
291             }
292         }
293         /* store 32-bit images as 24-bit images to save space. alpha terms
294            are lost */
295         else
296         {
297             DIBScanWidth = 3 * img.width;
298             if ( DIBScanWidth % 4 )
299                 DIBScanWidth += 4 - DIBScanWidth % 4;
300
301             for ( q = bits; q < bits + bit_size; q += DIBScanWidth,
302                                                  p += img.scan_width )
303             {
304                 t = p;
305                 for ( r = q; r < q + DIBScanWidth; r += 3, t += 4 )
306                     memcpy( (void *)r, (void *)t, 3 );
307             }
308         }
309     }
310
311     /* create the palette if necessary */
312     if ( img.palette != NULL )
313     {
314         pColor = (unsigned char *)calloc( img.palette_size, sizeof(RGBQUAD) );
315         if ( pColor == NULL )
316             longjmp( err_jmp, (int)errMemoryAllocation );
317
318         if ( img.bytes_per_palette_entry == 3 )
319         {
320             p = img.palette;
321             for ( q = pColor + 1; q < pColor +img.palette_size*sizeof(RGBQUAD);
322                             q += sizeof(RGBQUAD), p += 3 )
323             {
324                 memcpy( (void *)pColor, (void *)p, 3 );
325             }
326         }
327         else /* img.bytes_per_palette_entry == 4 */
328         {
329             memcpy( (void *)pColor, (void *)img.palette,
330                 img.palette_size * sizeof(RGBQUAD) );
331         }
332     }
333
334     /* now that we know how big everything is let's write the file */
335     memset( (void *)&bmfh, 0, sizeof(BITMAPFILEHEADER) );
336     bmfh.bfType = BMP_ID;
337     bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
338                      img.palette_size * sizeof(RGBQUAD);
339     bmfh.bfSize = bmfh.bfOffBits + bit_size;
340
341     if ( fwrite( (void *)&bmfh, sizeof(BITMAPFILEHEADER), 1, file ) != 1 )
342         longjmp( err_jmp, (int)errFileWrite );
343
344     if ( fwrite( (void *)&bmih, sizeof(BITMAPINFOHEADER), 1, file ) != 1 )
345         longjmp( err_jmp, (int)errFileWrite );
346
347     if ( pColor != NULL )
348     {
349         if ( fwrite( (void *)pColor, sizeof(RGBQUAD), img.palette_size, file )
350                               != (unsigned int)img.palette_size )
351         {
352             longjmp( err_jmp, (int)errFileWrite );
353         }
354     }
355
356     if ( fwrite( (void *)bits, sizeof(unsigned char), bit_size, file )
357                     != bit_size )
358     {
359         longjmp( err_jmp, (int)errFileWrite );
360     }
361
362     fclose( file );
363     free( bits );
364     if ( pColor != NULL )
365         free( pColor );
366     return BMG_OK;
367 }
368
369 #ifdef _NEVER_DEFINE_THIS_DEF_
370 /* following code is not tested.  I keep it here in case I ever find a BMP
371   file that is compressed and I want to test it */
372     else if ( bmih.biCompression == BI_RLE8 )
373     {
374         bmih.biCompression = BI_RGB;
375         bmih.biSizeImage = DIBScanWidth * img.height;
376         p = rawbits;
377         q = img.bits;
378         EOBMP = 1;
379         while ( q < img.bits + bit_size && p < rawbits + rawbit_size && EOBMP )
380         {
381             cnt = (unsigned int)*p;
382             p++;
383
384             /* encoded mode */
385             if ( cnt == 0 )
386             {
387                 cnt = (unsigned int)*p;
388                 if ( cnt < 3U )
389                 {
390                     p++;
391                     /* EOL */
392                     if ( *p == 0 )
393                         p++;
394                     /* end of bitmap */
395                     else if ( *p == 1 )
396                         EOBMP = 0;
397                     /* delta */
398                     else if ( *p == 2 )
399                     {
400                         p++;
401                         q += *p;  /* columns */
402                         p++;
403                         q += (*p)*BITScanWidth; /* rows */
404                         p++;
405                     }
406                 }
407                 /* copy *p duplicates of *(p++) into the bit array */
408                 else
409                 {
410                     cnt = (unsigned int)*p;
411                     p++;
412                     q_end = q + cnt;
413                     while ( q < q_end )
414                         *q++ = *p;
415                     p++;
416                 }
417             }
418             /* absolute mode */
419             else
420             {
421                 q_end = q + cnt;
422                 while ( q < q_end )
423                     *q++ = *p++;
424             }
425         }
426     }
427
428     /* if compression is desired && possible then attempt compression.  The
429     // following logic will try to compress the data.  If the compressed data
430     // requires more space than the uncompressed data then the bits will be
431     // stored in an uncompressed format */
432     if ( try_compression != 0 && img.bits_per_pixel == 8 )
433     {
434         p = rawbits;
435         r = bits;
436         new_bit_size = 0;
437         cnt = 0;
438         while ( p < rawbits + rawbit_size && new_bit_size < bit_size )
439         {
440             q = p;
441             while ( q < p + BITScanWidth )
442             {
443                 t = q;
444                 while ( t < q + 255 && t < p + BITScanWidth )
445                 {
446                     /* look for non-repeats - absolute mode */
447                     if ( *t != *(t+1) )
448                     {
449                         while ( *t != *(t+1) &&
450                                 cnt < 255 &&
451                                 t < p + BITScanWidth )
452                         {
453                             t++;
454                             cnt++;
455                         }
456                         cnt++;
457                         *r++ = (unsigned char)cnt;
458                         memcpy( (void *)r, (void *)q, cnt );
459                         r += cnt;
460                         q += cnt;
461                         new_bit_size += 1 + cnt;
462                         cnt = 0;
463                     }
464                     /* else look for repeats */
465                     else
466                     {
467                         while ( *t == *(t+1) &&
468                                 cnt < 255 &&
469                                 t < p + BITScanWidth )
470                         {
471                             t++;
472                             cnt++;
473                         }
474                         cnt++;
475                         if ( cnt > 2 )
476                         {
477                             *r++ = 0;
478                             *r++ = (unsigned char)cnt;
479                             *r++ = *(t-1);
480                             new_bit_size += 3;
481                             q = t;
482                             cnt = 0;
483                         }
484                         /* use absolute mode if we have reached the EOL &&
485                         // cnt <= 2 */
486                         else if ( t >= p + BITScanWidth )
487                         {
488                             *r++ = (unsigned char)cnt;
489                             memcpy( (void *)r, (void *)q, cnt );
490                             r += cnt;
491                             q += cnt;
492                             new_bit_size += 1 + cnt;
493                             cnt = 0;
494                         }
495                     }
496                 }
497
498                 /* put an EOL marker here */
499                 *r++ = 0;
500                 *r++ = 0;
501                 new_bit_size += 2;
502                 cnt = 0;
503             }
504
505             p += BITScanWidth;
506         }
507
508         /* put the EOB marker here */
509         if ( new_bit_size < bit_size - 2 )
510         {
511             *r++ = 0;
512             *r = 1;
513             new_bit_size += 2;
514         }
515         else
516             new_bit_size = bit_size + 1;
517
518         /* if the compressed image will take less space then use it */
519         if ( new_bit_size < bit_size )
520         {
521             bmih.biCompression = BI_RLE8;
522             bmih.biSizeImage = bit_size = new_bit_size;
523         }
524     }
525
526 #endif
527