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
littleendian(void)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
pixelconvert(unsigned char * srcbuf,enum BMPPIXELFORMAT srcformat,int srcpitch,unsigned char * dstbuf,enum BMPPIXELFORMAT dstformat,int dstpitch,int w,int h,int flip)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
loadppm(int * fd,unsigned char ** buf,int * w,int * h,enum BMPPIXELFORMAT f,int align,int dstbottomup,int ascii)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
loadbmp(char * filename,unsigned char ** buf,int * w,int * h,enum BMPPIXELFORMAT f,int align,int dstbottomup)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
saveppm(char * filename,unsigned char * buf,int w,int h,enum BMPPIXELFORMAT f,int srcpitch,int srcbottomup)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
savebmp(char * filename,unsigned char * buf,int w,int h,enum BMPPIXELFORMAT f,int srcpitch,int srcbottomup)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
bmpgeterr(void)395 const char *bmpgeterr(void)
396 {
397 return __bmperr;
398 }
399