Home | History | Annotate | Download | only in test
      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