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