• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C)2004 Landmark Graphics Corporation
2  * Copyright (C)2005 Sun Microsystems, Inc.
3  * Copyright (C)2010, 2012 D. R. Commander
4  *
5  * This library is free software and may be redistributed and/or modified under
6  * the terms of the wxWindows Library License, Version 3.1 or (at your option)
7  * any later version.  The full license is in the LICENSE.txt file included
8  * with this distribution.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * wxWindows Library License for more details.
14 */
15 
16 #include <fcntl.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #ifdef _WIN32
24  #include <io.h>
25 #else
26  #include <unistd.h>
27 #endif
28 #include "./tjutil.h"
29 #include "./bmp.h"
30 
31 #define byteswap(i) ( \
32 	(((i) & 0xff000000) >> 24) | \
33 	(((i) & 0x00ff0000) >>  8) | \
34 	(((i) & 0x0000ff00) <<  8) | \
35 	(((i) & 0x000000ff) << 24) )
36 
37 #define byteswap16(i) ( \
38 	(((i) & 0xff00) >> 8) | \
39 	(((i) & 0x00ff) << 8) )
40 
littleendian(void)41 static __inline int littleendian(void)
42 {
43 	unsigned int value=1;
44 	unsigned char *ptr=(unsigned char *)(&value);
45 	if(ptr[0]==1 && ptr[3]==0) return 1;
46 	else return 0;
47 }
48 
49 #ifndef BI_BITFIELDS
50 #define BI_BITFIELDS 3L
51 #endif
52 #ifndef BI_RGB
53 #define BI_RGB 0L
54 #endif
55 
56 #define BMPHDRSIZE 54
57 typedef struct _bmphdr
58 {
59 	unsigned short bfType;
60 	unsigned int bfSize;
61 	unsigned short bfReserved1, bfReserved2;
62 	unsigned int bfOffBits;
63 
64 	unsigned int biSize;
65 	int biWidth, biHeight;
66 	unsigned short biPlanes, biBitCount;
67 	unsigned int biCompression, biSizeImage;
68 	int biXPelsPerMeter, biYPelsPerMeter;
69 	unsigned int biClrUsed, biClrImportant;
70 } bmphdr;
71 
72 static const char *__bmperr="No error";
73 
74 static const int ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4};
75 static const int roffset[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1};
76 static const int goffset[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2};
77 static const int boffset[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3};
78 
79 #define _throw(m) {__bmperr=m;  retcode=-1;  goto finally;}
80 #define _unix(f) {if((f)==-1) _throw(strerror(errno));}
81 #define _catch(f) {if((f)==-1) {retcode=-1;  goto finally;}}
82 
83 #define readme(fd, addr, size) \
84 	if((bytesread=read(fd, addr, (size)))==-1) _throw(strerror(errno)); \
85 	if(bytesread!=(size)) _throw("Read error");
86 
pixelconvert(unsigned char * srcbuf,enum BMPPIXELFORMAT srcformat,int srcpitch,unsigned char * dstbuf,enum BMPPIXELFORMAT dstformat,int dstpitch,int w,int h,int flip)87 void pixelconvert(unsigned char *srcbuf, enum BMPPIXELFORMAT srcformat,
88 	int srcpitch, unsigned char *dstbuf, enum BMPPIXELFORMAT dstformat, int dstpitch,
89 	int w, int h, int flip)
90 {
91 	unsigned char *srcptr, *srcptr0, *dstptr, *dstptr0;
92 	int i, j;
93 
94 	srcptr=flip? &srcbuf[srcpitch*(h-1)]:srcbuf;
95 	for(j=0, dstptr=dstbuf; j<h; j++,
96 		srcptr+=flip? -srcpitch:srcpitch, dstptr+=dstpitch)
97 	{
98 		for(i=0, srcptr0=srcptr, dstptr0=dstptr; i<w; i++,
99 			srcptr0+=ps[srcformat], dstptr0+=ps[dstformat])
100 		{
101 			dstptr0[roffset[dstformat]]=srcptr0[roffset[srcformat]];
102 			dstptr0[goffset[dstformat]]=srcptr0[goffset[srcformat]];
103 			dstptr0[boffset[dstformat]]=srcptr0[boffset[srcformat]];
104 		}
105 	}
106 }
107 
loadppm(int * fd,unsigned char ** buf,int * w,int * h,enum BMPPIXELFORMAT f,int align,int dstbottomup,int ascii)108 int loadppm(int *fd, unsigned char **buf, int *w, int *h,
109 	enum BMPPIXELFORMAT f, int align, int dstbottomup, int ascii)
110 {
111 	FILE *fs=NULL;  int retcode=0, scalefactor, dstpitch;
112 	unsigned char *tempbuf=NULL;  char temps[255], temps2[255];
113 	int numread=0, totalread=0, pixel[3], i, j;
114 
115 	if((fs=fdopen(*fd, "r"))==NULL) _throw(strerror(errno));
116 
117 	do
118 	{
119 		if(!fgets(temps, 255, fs)) _throw("Read error");
120 		if(strlen(temps)==0 || temps[0]=='\n') continue;
121 		if(sscanf(temps, "%s", temps2)==1 && temps2[1]=='#') continue;
122 		switch(totalread)
123 		{
124 			case 0:
125 				if((numread=sscanf(temps, "%d %d %d", w, h, &scalefactor))==EOF)
126 					_throw("Read error");
127 				break;
128 			case 1:
129 				if((numread=sscanf(temps, "%d %d", h, &scalefactor))==EOF)
130 					_throw("Read error");
131 				break;
132 			case 2:
133 				if((numread=sscanf(temps, "%d", &scalefactor))==EOF)
134 					_throw("Read error");
135 				break;
136 		}
137 		totalread+=numread;
138 	} while(totalread<3);
139 	if((*w)<1 || (*h)<1 || scalefactor<1) _throw("Corrupt PPM header");
140 
141 	dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1));
142 	if((*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL)
143 		_throw("Memory allocation error");
144 	if(ascii)
145 	{
146 		for(j=0; j<*h; j++)
147 		{
148 			for(i=0; i<*w; i++)
149 			{
150 				if(fscanf(fs, "%d%d%d", &pixel[0], &pixel[1], &pixel[2])!=3)
151 					_throw("Read error");
152 				(*buf)[j*dstpitch+i*ps[f]+roffset[f]]=(unsigned char)(pixel[0]*255/scalefactor);
153 				(*buf)[j*dstpitch+i*ps[f]+goffset[f]]=(unsigned char)(pixel[1]*255/scalefactor);
154 				(*buf)[j*dstpitch+i*ps[f]+boffset[f]]=(unsigned char)(pixel[2]*255/scalefactor);
155 			}
156 		}
157 	}
158 	else
159 	{
160 		if(scalefactor!=255)
161 			_throw("Binary PPMs must have 8-bit components");
162 		if((tempbuf=(unsigned char *)malloc((*w)*(*h)*3))==NULL)
163 			_throw("Memory allocation error");
164 		if(fread(tempbuf, (*w)*(*h)*3, 1, fs)!=1) _throw("Read error");
165 		pixelconvert(tempbuf, BMP_RGB, (*w)*3, *buf, f, dstpitch, *w, *h, dstbottomup);
166 	}
167 
168 	finally:
169 	if(fs) {fclose(fs);  *fd=-1;}
170 	if(tempbuf) free(tempbuf);
171 	return retcode;
172 }
173 
174 
loadbmp(char * filename,unsigned char ** buf,int * w,int * h,enum BMPPIXELFORMAT f,int align,int dstbottomup)175 int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
176 	enum BMPPIXELFORMAT f, int align, int dstbottomup)
177 {
178 	int fd=-1, bytesread, srcpitch, srcbottomup=1, srcps, dstpitch,
179 		retcode=0;
180 	unsigned char *tempbuf=NULL;
181 	bmphdr bh;  int flags=O_RDONLY;
182 
183 	dstbottomup=dstbottomup? 1:0;
184 	#ifdef _WIN32
185 	flags|=O_BINARY;
186 	#endif
187 	if(!filename || !buf || !w || !h || f<0 || f>BMPPIXELFORMATS-1 || align<1)
188 		_throw("invalid argument to loadbmp()");
189 	if((align&(align-1))!=0)
190 		_throw("Alignment must be a power of 2");
191 	_unix(fd=open(filename, flags));
192 
193 	readme(fd, &bh.bfType, sizeof(unsigned short));
194 	if(!littleendian())	bh.bfType=byteswap16(bh.bfType);
195 
196 	if(bh.bfType==0x3650)
197 	{
198 		_catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 0));
199 		goto finally;
200 	}
201 	if(bh.bfType==0x3350)
202 	{
203 		_catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 1));
204 		goto finally;
205 	}
206 
207 	readme(fd, &bh.bfSize, sizeof(unsigned int));
208 	readme(fd, &bh.bfReserved1, sizeof(unsigned short));
209 	readme(fd, &bh.bfReserved2, sizeof(unsigned short));
210 	readme(fd, &bh.bfOffBits, sizeof(unsigned int));
211 	readme(fd, &bh.biSize, sizeof(unsigned int));
212 	readme(fd, &bh.biWidth, sizeof(int));
213 	readme(fd, &bh.biHeight, sizeof(int));
214 	readme(fd, &bh.biPlanes, sizeof(unsigned short));
215 	readme(fd, &bh.biBitCount, sizeof(unsigned short));
216 	readme(fd, &bh.biCompression, sizeof(unsigned int));
217 	readme(fd, &bh.biSizeImage, sizeof(unsigned int));
218 	readme(fd, &bh.biXPelsPerMeter, sizeof(int));
219 	readme(fd, &bh.biYPelsPerMeter, sizeof(int));
220 	readme(fd, &bh.biClrUsed, sizeof(unsigned int));
221 	readme(fd, &bh.biClrImportant, sizeof(unsigned int));
222 
223 	if(!littleendian())
224 	{
225 		bh.bfSize=byteswap(bh.bfSize);
226 		bh.bfOffBits=byteswap(bh.bfOffBits);
227 		bh.biSize=byteswap(bh.biSize);
228 		bh.biWidth=byteswap(bh.biWidth);
229 		bh.biHeight=byteswap(bh.biHeight);
230 		bh.biPlanes=byteswap16(bh.biPlanes);
231 		bh.biBitCount=byteswap16(bh.biBitCount);
232 		bh.biCompression=byteswap(bh.biCompression);
233 		bh.biSizeImage=byteswap(bh.biSizeImage);
234 		bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter);
235 		bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter);
236 		bh.biClrUsed=byteswap(bh.biClrUsed);
237 		bh.biClrImportant=byteswap(bh.biClrImportant);
238 	}
239 
240 	if(bh.bfType!=0x4d42 || bh.bfOffBits<BMPHDRSIZE
241 	|| bh.biWidth<1 || bh.biHeight==0)
242 		_throw("Corrupt bitmap header");
243 	if((bh.biBitCount!=24 && bh.biBitCount!=32) || bh.biCompression!=BI_RGB)
244 		_throw("Only uncompessed RGB bitmaps are supported");
245 
246 	*w=bh.biWidth;  *h=bh.biHeight;  srcps=bh.biBitCount/8;
247 	if(*h<0) {*h=-(*h);  srcbottomup=0;}
248 	srcpitch=(((*w)*srcps)+3)&(~3);
249 	dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1));
250 
251 	if(srcpitch*(*h)+bh.bfOffBits!=bh.bfSize) _throw("Corrupt bitmap header");
252 	if((tempbuf=(unsigned char *)malloc(srcpitch*(*h)))==NULL
253 	|| (*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL)
254 		_throw("Memory allocation error");
255 	if(lseek(fd, (long)bh.bfOffBits, SEEK_SET)!=(long)bh.bfOffBits)
256 		_throw(strerror(errno));
257 	_unix(bytesread=read(fd, tempbuf, srcpitch*(*h)));
258 	if(bytesread!=srcpitch*(*h)) _throw("Read error");
259 
260 	pixelconvert(tempbuf, BMP_BGR, srcpitch, *buf, f, dstpitch, *w, *h,
261 		srcbottomup!=dstbottomup);
262 
263 	finally:
264 	if(tempbuf) free(tempbuf);
265 	if(fd!=-1) close(fd);
266 	return retcode;
267 }
268 
269 #define writeme(fd, addr, size) \
270 	if((byteswritten=write(fd, addr, (size)))==-1) _throw(strerror(errno)); \
271 	if(byteswritten!=(size)) _throw("Write error");
272 
saveppm(char * filename,unsigned char * buf,int w,int h,enum BMPPIXELFORMAT f,int srcpitch,int srcbottomup)273 int saveppm(char *filename, unsigned char *buf, int w, int h,
274 	enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup)
275 {
276 	FILE *fs=NULL;  int retcode=0;
277 	unsigned char *tempbuf=NULL;
278 
279 	if((fs=fopen(filename, "wb"))==NULL) _throw(strerror(errno));
280 	if(fprintf(fs, "P6\n")<1) _throw("Write error");
281 	if(fprintf(fs, "%d %d\n", w, h)<1) _throw("Write error");
282 	if(fprintf(fs, "255\n")<1) _throw("Write error");
283 
284 	if((tempbuf=(unsigned char *)malloc(w*h*3))==NULL)
285 		_throw("Memory allocation error");
286 
287 	pixelconvert(buf, f, srcpitch, tempbuf, BMP_RGB, w*3, w, h,
288 		srcbottomup);
289 
290 	if((fwrite(tempbuf, w*h*3, 1, fs))!=1) _throw("Write error");
291 
292 	finally:
293 	if(tempbuf) free(tempbuf);
294 	if(fs) fclose(fs);
295 	return retcode;
296 }
297 
savebmp(char * filename,unsigned char * buf,int w,int h,enum BMPPIXELFORMAT f,int srcpitch,int srcbottomup)298 int savebmp(char *filename, unsigned char *buf, int w, int h,
299 	enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup)
300 {
301 	int fd=-1, byteswritten, dstpitch, retcode=0;
302 	int flags=O_RDWR|O_CREAT|O_TRUNC;
303 	unsigned char *tempbuf=NULL;  char *temp;
304 	bmphdr bh;  int mode;
305 
306 	#ifdef _WIN32
307 	flags|=O_BINARY;  mode=_S_IREAD|_S_IWRITE;
308 	#else
309 	mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
310 	#endif
311 	if(!filename || !buf || w<1 || h<1 || f<0 || f>BMPPIXELFORMATS-1 || srcpitch<0)
312 		_throw("bad argument to savebmp()");
313 
314 	if(srcpitch==0) srcpitch=w*ps[f];
315 
316 	if((temp=strrchr(filename, '.'))!=NULL)
317 	{
318 		if(!strcasecmp(temp, ".ppm"))
319 			return saveppm(filename, buf, w, h, f, srcpitch, srcbottomup);
320 	}
321 
322 	_unix(fd=open(filename, flags, mode));
323 	dstpitch=((w*3)+3)&(~3);
324 
325 	bh.bfType=0x4d42;
326 	bh.bfSize=BMPHDRSIZE+dstpitch*h;
327 	bh.bfReserved1=0;  bh.bfReserved2=0;
328 	bh.bfOffBits=BMPHDRSIZE;
329 	bh.biSize=40;
330 	bh.biWidth=w;  bh.biHeight=h;
331 	bh.biPlanes=0;  bh.biBitCount=24;
332 	bh.biCompression=BI_RGB;  bh.biSizeImage=0;
333 	bh.biXPelsPerMeter=0;  bh.biYPelsPerMeter=0;
334 	bh.biClrUsed=0;  bh.biClrImportant=0;
335 
336 	if(!littleendian())
337 	{
338 		bh.bfType=byteswap16(bh.bfType);
339 		bh.bfSize=byteswap(bh.bfSize);
340 		bh.bfOffBits=byteswap(bh.bfOffBits);
341 		bh.biSize=byteswap(bh.biSize);
342 		bh.biWidth=byteswap(bh.biWidth);
343 		bh.biHeight=byteswap(bh.biHeight);
344 		bh.biPlanes=byteswap16(bh.biPlanes);
345 		bh.biBitCount=byteswap16(bh.biBitCount);
346 		bh.biCompression=byteswap(bh.biCompression);
347 		bh.biSizeImage=byteswap(bh.biSizeImage);
348 		bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter);
349 		bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter);
350 		bh.biClrUsed=byteswap(bh.biClrUsed);
351 		bh.biClrImportant=byteswap(bh.biClrImportant);
352 	}
353 
354 	writeme(fd, &bh.bfType, sizeof(unsigned short));
355 	writeme(fd, &bh.bfSize, sizeof(unsigned int));
356 	writeme(fd, &bh.bfReserved1, sizeof(unsigned short));
357 	writeme(fd, &bh.bfReserved2, sizeof(unsigned short));
358 	writeme(fd, &bh.bfOffBits, sizeof(unsigned int));
359 	writeme(fd, &bh.biSize, sizeof(unsigned int));
360 	writeme(fd, &bh.biWidth, sizeof(int));
361 	writeme(fd, &bh.biHeight, sizeof(int));
362 	writeme(fd, &bh.biPlanes, sizeof(unsigned short));
363 	writeme(fd, &bh.biBitCount, sizeof(unsigned short));
364 	writeme(fd, &bh.biCompression, sizeof(unsigned int));
365 	writeme(fd, &bh.biSizeImage, sizeof(unsigned int));
366 	writeme(fd, &bh.biXPelsPerMeter, sizeof(int));
367 	writeme(fd, &bh.biYPelsPerMeter, sizeof(int));
368 	writeme(fd, &bh.biClrUsed, sizeof(unsigned int));
369 	writeme(fd, &bh.biClrImportant, sizeof(unsigned int));
370 
371 	if((tempbuf=(unsigned char *)malloc(dstpitch*h))==NULL)
372 		_throw("Memory allocation error");
373 
374 	pixelconvert(buf, f, srcpitch, tempbuf, BMP_BGR, dstpitch, w, h,
375 		!srcbottomup);
376 
377 	if((byteswritten=write(fd, tempbuf, dstpitch*h))!=dstpitch*h)
378 		_throw(strerror(errno));
379 
380 	finally:
381 	if(tempbuf) free(tempbuf);
382 	if(fd!=-1) close(fd);
383 	return retcode;
384 }
385 
bmpgeterr(void)386 const char *bmpgeterr(void)
387 {
388 	return __bmperr;
389 }
390