Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 : #include "gimage.h"
28 : #include "gimagebmpP.h"
29 : #include <string.h>
30 : GImage *_GImage_Create(enum image_type type, int32 width, int32 height);
31 :
32 0 : static int getshort(FILE *fp) {
33 : /* Get Little-Endian short 16bit value. Return value if okay, -1 if error */
34 : int ch1, ch2;
35 :
36 0 : if ( (ch1=fgetc(fp))<0 || (ch2=fgetc(fp))<0 )
37 0 : return( -1 );
38 :
39 0 : return( (ch2<<8) | ch1 );
40 : }
41 :
42 0 : static long getlong(FILE *fp, long *value) {
43 : /* Get Little-Endian long 32bit int value. Return 0 if okay, -1 if error. */
44 : int ch1, ch2, ch3, ch4;
45 :
46 0 : if ( (ch1=fgetc(fp))<0 || (ch2=fgetc(fp))<0 || \
47 0 : (ch3=fgetc(fp))<0 || (ch4=fgetc(fp))<0 ) {
48 0 : *value=0;
49 0 : return( -1 );
50 : }
51 0 : *value=(long)( (ch4<<24)|(ch3<<16)|(ch2<<8)|ch1 );
52 0 : return( 0 );
53 : }
54 :
55 0 : static int bitshift(unsigned long mask) {
56 0 : int off=0, len=0, bit;
57 :
58 0 : if ( mask==0 )
59 0 : return( 0 );
60 0 : for ( off=0; !(mask&1); mask>>=1, ++off );
61 0 : for ( len=0, bit=1; (mask&bit) && len<32; ++len, bit<<=1 );
62 0 : return( off+(8-len) );
63 : }
64 :
65 0 : static int fillbmpheader(FILE *fp,struct bmpheader *head) {
66 : /* Get BMPheader info. Return 0 if read the header okay, -1 if read error */
67 : int i;
68 : long temp;
69 :
70 0 : if ( fgetc(fp)!='B' || getc(fp)!='M' || /* Bad format */ \
71 0 : getlong(fp,&head->size) || \
72 0 : (head->mbz1=getshort(fp))<0 || \
73 0 : (head->mbz2=getshort(fp))<0 || \
74 0 : getlong(fp,&head->offset) || \
75 0 : getlong(fp,&head->headersize) )
76 0 : return( -1 );
77 :
78 0 : if ( head->headersize==12 ) { /* Windows 2.0 format, also OS/2 */
79 0 : if ( (head->width=getshort(fp))<0 || \
80 0 : (head->height=getshort(fp))<0 || \
81 0 : (head->planes=getshort(fp))<0 || \
82 0 : (head->bitsperpixel=getshort(fp))<0 )
83 0 : return( -1 );
84 : //head->colorsused=0;
85 : //head->compression=0;
86 : } else {
87 0 : if ( getlong(fp,&head->width) || \
88 0 : getlong(fp,&head->height) || \
89 0 : (head->planes=getshort(fp))<0 || \
90 0 : (head->bitsperpixel=getshort(fp))<0 || \
91 0 : getlong(fp,&head->compression) || \
92 0 : getlong(fp,&head->imagesize) || \
93 0 : getlong(fp,&head->ignore1) || \
94 0 : getlong(fp,&head->ignore2) || \
95 0 : getlong(fp,&head->colorsused) || \
96 0 : getlong(fp,&head->colorsimportant) )
97 0 : return( -1 );
98 : }
99 0 : if ( head->height<0 )
100 0 : head->height = -head->height;
101 : else
102 0 : head->invert = true;
103 :
104 0 : if ( head->bitsperpixel!=1 && head->bitsperpixel!=4 && head->bitsperpixel!=8 &&
105 0 : head->bitsperpixel!=16 && head->bitsperpixel!=24 && head->bitsperpixel!=32 )
106 0 : return( -1 );
107 :
108 0 : if ( head->compression==3 && ( head->bitsperpixel==16 || head->bitsperpixel==32 ) )
109 : /* Good */;
110 0 : else if ( head->compression==0 && ( head->bitsperpixel<=8 || head->bitsperpixel==24 || head->bitsperpixel==32 ) )
111 : /* Good */;
112 0 : else if ( head->compression==1 && head->bitsperpixel==8 )
113 : /* Good */;
114 0 : else if ( head->compression==2 && head->bitsperpixel==4 )
115 : /* Good */;
116 : else
117 0 : return( -1 );
118 :
119 0 : if ( head->colorsused==0 )
120 0 : head->colorsused = 1<<head->bitsperpixel;
121 0 : if ( head->bitsperpixel>=16 )
122 0 : head->colorsused = 0;
123 0 : if ( head->colorsused>(1<<head->bitsperpixel) )
124 0 : return( -1 );
125 :
126 0 : for ( i=0; i<head->colorsused; ++i ) {
127 : int b,g,r;
128 0 : if ( (b=fgetc(fp))<0 || (g=fgetc(fp))<0 || (r=fgetc(fp))<0 )
129 0 : return( -1 );
130 0 : head->clut[i]=COLOR_CREATE(r,g,b);
131 0 : if ( head->headersize!=12 && fgetc(fp)<0 )
132 0 : return( -1 );
133 : }
134 0 : if ( head->compression==3 || head->headersize==108 ) {
135 0 : if ( getlong(fp,&head->red_mask) || \
136 0 : getlong(fp,&head->green_mask) || \
137 0 : getlong(fp,&head->blue_mask) )
138 0 : return( -1 );
139 0 : head->red_shift = bitshift(head->red_mask);
140 0 : head->green_shift = bitshift(head->green_mask);
141 0 : head->blue_shift = bitshift(head->blue_mask);
142 : }
143 :
144 0 : if ( head->headersize==108 && (
145 0 : getlong(fp,&temp) || /* alpha_mask */ \
146 0 : getlong(fp,&temp) || /* color space type */ \
147 0 : getlong(fp,&temp) || /* redx */ \
148 0 : getlong(fp,&temp) || /* redy */ \
149 0 : getlong(fp,&temp) || /* redz */ \
150 0 : getlong(fp,&temp) || /* greenx */ \
151 0 : getlong(fp,&temp) || /* greeny */ \
152 0 : getlong(fp,&temp) || /* greenz */ \
153 0 : getlong(fp,&temp) || /* bluex */ \
154 0 : getlong(fp,&temp) || /* bluey */ \
155 0 : getlong(fp,&temp) || /* bluez */ \
156 0 : getlong(fp,&temp) || /* gammared */ \
157 0 : getlong(fp,&temp) || /* gammagreen */ \
158 0 : getlong(fp,&temp) ) /* gammablue */ )
159 0 : return( -1 );
160 :
161 0 : return( 0 );
162 : }
163 :
164 0 : static int readpixels(FILE *file,struct bmpheader *head) {
165 : int i,ii,j,ll,excess;
166 :
167 0 : fseek(file,head->offset,0);
168 :
169 0 : ll = head->width;
170 0 : if ( head->bitsperpixel==8 && head->compression==0 ) {
171 0 : excess = ((ll+3)/4)*4 -ll;
172 0 : for ( i=0; i<head->height; ++i ) {
173 0 : fread(head->byte_pixels+i*ll,1,ll,file);
174 0 : for ( j=0; j<excess; ++j )
175 0 : (void) getc(file);
176 : }
177 0 : } else if ( head->bitsperpixel==8 ) {
178 : /* 8 bit RLE */
179 0 : int ii = 0;
180 0 : while ( ii<head->height*head->width ) {
181 0 : int cnt = getc(file);
182 0 : if ( cnt!=0 ) {
183 0 : int ch = getc(file);
184 0 : while ( --cnt>=0 )
185 0 : head->byte_pixels[ii++] = ch;
186 : } else {
187 0 : cnt = getc(file);
188 0 : if ( cnt>= 3 ) {
189 0 : int odd = cnt&1;
190 0 : while ( --cnt>=0 )
191 0 : head->byte_pixels[ii++] = getc(file);
192 0 : if ( odd )
193 0 : getc(file);
194 0 : } else if ( cnt==0 ) { /* end of scan line */
195 0 : ii = ((ii+head->width-1)/head->width) * head->width;
196 0 : } else if ( cnt==1 ) {
197 0 : break;
198 0 : } else if ( cnt==2 ) {
199 0 : int x=getc(file);
200 0 : int y = getc(file);
201 0 : y += ii/head->width;
202 0 : x += ii%head->width;
203 0 : ii = y*head->width + x;
204 : }
205 : }
206 : }
207 0 : } else if ( head->bitsperpixel==4 && head->compression==0 ) {
208 0 : excess = (ll+1)/2;
209 0 : excess = ((excess+3)/4)*4 -excess;
210 0 : for ( i=0; i<head->height; ++i ) {
211 0 : ii = i*ll;
212 0 : for ( j=0; j<((head->width+7)/8)*8; j+=8 ) {
213 0 : int b1 = getc(file);
214 0 : int b2 = getc(file);
215 0 : int b3 = getc(file);
216 0 : int b4 = getc(file);
217 0 : head->byte_pixels[ii+j] = (b1>>4);
218 0 : if ( j+1<ll ) head->byte_pixels[ii+j+1] = (b1&0xf);
219 0 : if ( j+2<ll ) head->byte_pixels[ii+j+2] = (b2>>4);
220 0 : if ( j+3<ll ) head->byte_pixels[ii+j+3] = (b2&0xf);
221 0 : if ( j+4<ll ) head->byte_pixels[ii+j+4] = (b3>>4);
222 0 : if ( j+5<ll ) head->byte_pixels[ii+j+5] = (b3&0xf);
223 0 : if ( j+6<ll ) head->byte_pixels[ii+j+6] = (b4>>4);
224 0 : if ( j+7<ll ) head->byte_pixels[ii+j+7] = (b4&0xf);
225 : }
226 0 : for ( j=0; j<excess; ++j )
227 0 : (void) getc(file);
228 : }
229 0 : } else if ( head->bitsperpixel==4 ) {
230 : /* 4 bit RLE */
231 0 : int ii = 0;
232 0 : while ( ii<head->height*head->width ) {
233 0 : int cnt = getc(file);
234 0 : if ( cnt!=0 ) {
235 0 : int ch = getc(file);
236 0 : while ( (cnt-=2)>=-1 ) {
237 0 : head->byte_pixels[ii++] = ch>>4;
238 0 : head->byte_pixels[ii++] = ch&0xf;
239 : }
240 0 : if ( cnt==-1 ) --ii;
241 : } else {
242 0 : cnt = getc(file);
243 0 : if ( cnt>= 3 ) {
244 0 : int odd = cnt&2;
245 0 : while ( (cnt-=2)>=-1 ) {
246 0 : int ch = getc(file);
247 0 : head->byte_pixels[ii++] = ch>>4;
248 0 : head->byte_pixels[ii++] = ch&0xf;
249 : }
250 0 : if ( cnt==-1 ) --ii;
251 0 : if ( odd )
252 0 : getc(file);
253 0 : } else if ( cnt==0 ) { /* end of scan line */
254 0 : ii = ((ii+head->width-1)/head->width) * head->width;
255 0 : } else if ( cnt==1 ) {
256 0 : break;
257 0 : } else if ( cnt==2 ) {
258 0 : int x=getc(file);
259 0 : int y = getc(file);
260 0 : y += ii/head->width;
261 0 : x += ii%head->width;
262 0 : ii = y*head->width + x;
263 : }
264 : }
265 : }
266 0 : } else if ( head->bitsperpixel==1 ) {
267 0 : excess = (ll+7)/8;
268 0 : excess = ((excess+3)/4)*4 -excess;
269 0 : for ( i=0; i<head->height; ++i ) {
270 0 : ii = i*((ll+7)/8);
271 0 : for ( j=0; j<((head->width+7)/8); ++j ) {
272 0 : head->byte_pixels[ii+j] = getc(file);
273 : }
274 0 : for ( j=0; j<excess; ++j )
275 0 : (void) getc(file);
276 : }
277 0 : } else if ( head->bitsperpixel==24 ) {
278 0 : excess = ((3*head->width+3)/4)*4 - 3*head->width;
279 0 : for ( i=0; i<head->height; ++i ) {
280 0 : ii = i*head->width;
281 0 : for ( j=0; j<head->width; ++j ) {
282 0 : int b = getc(file);
283 0 : int g = getc(file);
284 0 : int r = getc(file);
285 0 : head->int32_pixels[ii+j] = COLOR_CREATE(r,g,b);
286 : }
287 0 : for ( j=0; j<excess; ++j )
288 0 : (void) getc(file); /* ignore padding */
289 : }
290 0 : } else if ( head->bitsperpixel==32 && head->compression==0 ) {
291 0 : for ( i=0; i<head->height; ++i ) {
292 0 : ii = i*head->width;
293 0 : for ( j=0; j<head->width; ++j ) {
294 : int b,g,r;
295 0 : b = getc(file);
296 0 : g = getc(file);
297 0 : r = getc(file);
298 0 : (void) getc(file); /* Ignore the alpha channel */
299 0 : head->int32_pixels[ii+j] = COLOR_CREATE(r,g,b);
300 : }
301 : }
302 0 : } else if ( head->bitsperpixel==16 ) {
303 0 : for ( i=0; i<head->height; ++i ) {
304 0 : ii = i*head->width;
305 0 : for ( j=0; j<head->width; ++j ) {
306 0 : int pix = getshort(file);
307 0 : head->int32_pixels[ii+j] = COLOR_CREATE((pix&head->red_mask)>>head->red_shift,
308 : (pix&head->green_mask)>>head->green_shift,
309 : (pix&head->blue_mask)>>head->blue_shift);
310 : }
311 : }
312 0 : if ( head->width&1 )
313 0 : getshort(file);
314 0 : } else if ( head->bitsperpixel==32 ) {
315 0 : for ( i=0; i<head->height; ++i ) {
316 0 : ii = i*head->width;
317 0 : for ( j=0; j<head->width; ++j ) {
318 : long pix;
319 0 : if ( getlong(file,&pix) )
320 0 : return( 1 );
321 0 : head->int32_pixels[ii+j] = COLOR_CREATE((pix&head->red_mask)>>head->red_shift,
322 : (pix&head->green_mask)>>head->green_shift,
323 : (pix&head->blue_mask)>>head->blue_shift);
324 : }
325 : }
326 : }
327 :
328 0 : if ( feof(file )) { /* Did we get an incomplete file? */
329 0 : return( 0 );
330 : }
331 :
332 0 : return( 1 );
333 : }
334 :
335 0 : GImage *GImageRead_Bmp(FILE *file) {
336 : /* Import a BMP image (based on file handle), cleanup & return NULL if error */
337 : struct bmpheader bmp;
338 : int i,l;
339 0 : GImage *ret = NULL;
340 : struct _GImage *base;
341 :
342 0 : if ( file==NULL )
343 0 : return( NULL );
344 :
345 : /* First, read-in header information */
346 0 : memset(&bmp,'\0',sizeof(bmp));
347 0 : if ( fillbmpheader(file,&bmp) )
348 0 : goto errorGImageReadBmp;
349 :
350 : /* Create memory-space to read-in bmp file */
351 0 : if ( (bmp.bitsperpixel>=16 && \
352 0 : (bmp.int32_pixels=(uint32 *)(malloc(bmp.height*bmp.width*sizeof(uint32))))==NULL) || \
353 0 : (bmp.bitsperpixel==1 && \
354 0 : (bmp.byte_pixels=(unsigned char *)(malloc(bmp.height*((bmp.width+7)/8)*sizeof(unsigned char))))==NULL) || \
355 0 : (bmp.byte_pixels=(unsigned char *)(malloc(bmp.height*bmp.width*sizeof(unsigned char))))==NULL ) {
356 0 : NoMoreMemMessage();
357 0 : return( NULL );
358 : }
359 :
360 0 : if ( !readpixels(file,&bmp) )
361 0 : goto errorGImageReadBmp;
362 :
363 0 : if ( !bmp.invert ) {
364 0 : if ( (ret=_GImage_Create(bmp.bitsperpixel>=16?it_true:bmp.bitsperpixel!=1?it_index:it_mono,
365 0 : bmp.width, bmp.height))==NULL ) {
366 0 : NoMoreMemMessage();
367 0 : goto errorGImageMemBmp;
368 : }
369 0 : if ( bmp.bitsperpixel>=16 ) {
370 0 : ret->u.image->data = (uint8 *) bmp.int32_pixels;
371 0 : } else if ( bmp.bitsperpixel!=1 ) {
372 0 : ret->u.image->data = (uint8 *) bmp.byte_pixels;
373 : }
374 : } else {
375 0 : if ( bmp.bitsperpixel>=16 ) {
376 0 : if ( (ret=GImageCreate(it_true,bmp.width, bmp.height))==NULL ) {
377 0 : NoMoreMemMessage();
378 0 : goto errorGImageMemBmp;
379 : }
380 0 : base = ret->u.image;
381 0 : for ( i=0; i<bmp.height; ++i ) {
382 0 : l = bmp.height-1-i;
383 0 : memcpy(base->data+l*base->bytes_per_line,bmp.int32_pixels+i*bmp.width,bmp.width*sizeof(uint32));
384 : }
385 0 : free(bmp.int32_pixels);
386 0 : } else if ( bmp.bitsperpixel!=1 ) {
387 0 : if ( (ret=GImageCreate(it_index,bmp.width, bmp.height))==NULL ) {
388 0 : NoMoreMemMessage();
389 0 : goto errorGImageMemBmp;
390 : }
391 0 : base = ret->u.image;
392 0 : for ( i=0; i<bmp.height; ++i ) {
393 0 : l = bmp.height-1-i;
394 0 : memcpy(base->data+l*base->bytes_per_line,bmp.byte_pixels+i*bmp.width,bmp.width);
395 : }
396 0 : free(bmp.byte_pixels);
397 : } else {
398 0 : if ( (ret=GImageCreate(it_mono,bmp.width, bmp.height))==NULL ) {
399 0 : NoMoreMemMessage();
400 0 : goto errorGImageMemBmp;
401 : }
402 0 : base = ret->u.image;
403 0 : for ( i=0; i<bmp.height; ++i ) {
404 0 : l = bmp.height-1-i;
405 0 : memcpy(base->data+l*base->bytes_per_line,bmp.byte_pixels+i*base->bytes_per_line,base->bytes_per_line);
406 : }
407 0 : free(bmp.byte_pixels);
408 : }
409 : }
410 0 : if ( ret->u.image->image_type==it_index ) {
411 0 : ret->u.image->clut->clut_len = bmp.colorsused;
412 0 : memcpy(ret->u.image->clut->clut,bmp.clut,bmp.colorsused*sizeof(Color));
413 0 : ret->u.image->clut->trans_index = COLOR_UNKNOWN;
414 0 : } else if ( ret->u.image->image_type==it_mono && bmp.colorsused!=0 ) {
415 0 : if ( (ret->u.image->clut=(GClut *)(calloc(1,sizeof(GClut))))==NULL ) {
416 0 : NoMoreMemMessage();
417 0 : goto errorGImageMemBmp;
418 : }
419 0 : ret->u.image->clut->clut_len = bmp.colorsused;
420 0 : memcpy(ret->u.image->clut->clut,bmp.clut,bmp.colorsused*sizeof(Color));
421 0 : ret->u.image->clut->trans_index = COLOR_UNKNOWN;
422 : }
423 0 : return( ret );
424 :
425 : errorGImageReadBmp:
426 0 : fprintf(stderr,"Bad input file\n");
427 : errorGImageMemBmp:
428 0 : GImageDestroy(ret);
429 0 : if ( bmp.bitsperpixel>=16 ) free(bmp.int32_pixels);
430 0 : else free(bmp.byte_pixels);
431 0 : return( NULL );
432 : }
433 :
434 0 : GImage *GImageReadBmp(char *filename) {
435 : /* Import a BMP image, else cleanup and return NULL if error found */
436 : FILE *file; /* source file */
437 : GImage *ret;
438 :
439 0 : if ( (file=fopen(filename,"rb"))==NULL ) {
440 0 : fprintf(stderr,"Can't open \"%s\"\n", filename);
441 0 : return( NULL );
442 : }
443 :
444 0 : ret = GImageRead_Bmp(file);
445 0 : fclose(file);
446 0 : return( ret );
447 : }
|