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