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