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