GLES2RICE: Update from upstream
[mupen64plus-pandora.git] / source / gles2rice / src / liblinux / bmp.c
... / ...
CommitLineData
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
38static 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*/
57BMGError 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 */
180err_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*/
205BMGError 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