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