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 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 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 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 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 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 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 386 const char *bmpgeterr(void) 387 { 388 return __bmperr; 389 } 390