xref: /plan9-contrib/sys/src/cmd/jpg/readbmp.c (revision c93608cc76758b2be624199c6208a0f90bad298d)
1fcdc259eSDavid du Colombier #include <u.h>
2fcdc259eSDavid du Colombier #include <libc.h>
3fcdc259eSDavid du Colombier #include <bio.h>
4fcdc259eSDavid du Colombier #include <draw.h>
5fcdc259eSDavid du Colombier #include "imagefile.h"
6fcdc259eSDavid du Colombier #include "bmp.h"
7fcdc259eSDavid du Colombier 
8fcdc259eSDavid du Colombier /*
9fcdc259eSDavid du Colombier  MS-BMP file reader
10fcdc259eSDavid du Colombier  (c) 2003, I.P.Keller
11fcdc259eSDavid du Colombier 
12fcdc259eSDavid du Colombier  aims to decode *all* valid bitmap formats, although some of the
13fcdc259eSDavid du Colombier  flavours couldn't be verified due to lack of suitable test-files.
14fcdc259eSDavid du Colombier  the following flavours are supported:
15fcdc259eSDavid du Colombier 
16fcdc259eSDavid du Colombier 	Bit/Pix	Orientation	Compression	Tested?
17fcdc259eSDavid du Colombier 	  1	top->bottom	n/a		yes
18fcdc259eSDavid du Colombier 	  1	bottom->top	n/a		yes
19fcdc259eSDavid du Colombier 	  4	top->bottom	no		yes
20fcdc259eSDavid du Colombier 	  4	bottom->top	no		yes
21fcdc259eSDavid du Colombier 	  4	top->bottom	RLE4		yes, but not with displacement
22fcdc259eSDavid du Colombier 	  8	top->bottom	no		yes
23fcdc259eSDavid du Colombier 	  8	bottom->top	no		yes
24fcdc259eSDavid du Colombier 	  8	top->bottom	RLE8		yes, but not with displacement
25fcdc259eSDavid du Colombier 	 16	top->bottom	no		no
26fcdc259eSDavid du Colombier 	 16	bottom->top	no		no
27fcdc259eSDavid du Colombier 	 16	top->bottom	BITMASK		no
28fcdc259eSDavid du Colombier 	 16	bottom->top	BITMASK		no
29fcdc259eSDavid du Colombier 	 24	top->bottom	n/a		yes
30fcdc259eSDavid du Colombier 	 24	bottom->top	n/a		yes
31fcdc259eSDavid du Colombier 	 32	top->bottom	no		no
32fcdc259eSDavid du Colombier 	 32	bottom->top	no		no
33fcdc259eSDavid du Colombier 	 32	top->bottom	BITMASK		no
34fcdc259eSDavid du Colombier 	 32	bottom->top	BITMASK		no
35fcdc259eSDavid du Colombier 
36fcdc259eSDavid du Colombier  OS/2 1.x bmp files are recognised as well, but testing was very limited.
37fcdc259eSDavid du Colombier 
38fcdc259eSDavid du Colombier  verifying was done with a number of test files, generated by
39fcdc259eSDavid du Colombier  different tools. nevertheless, the tests were in no way exhaustive
40fcdc259eSDavid du Colombier  enough to guarantee bug-free decoding. caveat emptor!
41fcdc259eSDavid du Colombier */
42fcdc259eSDavid du Colombier 
43fcdc259eSDavid du Colombier static short
r16(Biobuf * b)44fcdc259eSDavid du Colombier r16(Biobuf*b)
45fcdc259eSDavid du Colombier {
46fcdc259eSDavid du Colombier 	short s;
47fcdc259eSDavid du Colombier 
48fcdc259eSDavid du Colombier 	s = Bgetc(b);
49fcdc259eSDavid du Colombier 	s |= ((short)Bgetc(b)) << 8;
50fcdc259eSDavid du Colombier 	return s;
51fcdc259eSDavid du Colombier }
52fcdc259eSDavid du Colombier 
53fcdc259eSDavid du Colombier 
54fcdc259eSDavid du Colombier static long
r32(Biobuf * b)55fcdc259eSDavid du Colombier r32(Biobuf*b)
56fcdc259eSDavid du Colombier {
57fcdc259eSDavid du Colombier 	long l;
58fcdc259eSDavid du Colombier 
59fcdc259eSDavid du Colombier 	l = Bgetc(b);
60fcdc259eSDavid du Colombier 	l |= ((long)Bgetc(b)) << 8;
61fcdc259eSDavid du Colombier 	l |= ((long)Bgetc(b)) << 16;
62fcdc259eSDavid du Colombier 	l |= ((long)Bgetc(b)) << 24;
63fcdc259eSDavid du Colombier 	return l;
64fcdc259eSDavid du Colombier }
65fcdc259eSDavid du Colombier 
66fcdc259eSDavid du Colombier 
67fcdc259eSDavid du Colombier /* get highest bit set */
68fcdc259eSDavid du Colombier static int
msb(ulong x)69fcdc259eSDavid du Colombier msb(ulong x)
70fcdc259eSDavid du Colombier {
71fcdc259eSDavid du Colombier 	int i;
72fcdc259eSDavid du Colombier 	for(i = 32; i; i--, x <<= 1)
73fcdc259eSDavid du Colombier 		if(x & 0x80000000L)
74fcdc259eSDavid du Colombier 			return i;
75fcdc259eSDavid du Colombier 	return 0;
76fcdc259eSDavid du Colombier }
77fcdc259eSDavid du Colombier 
78fcdc259eSDavid du Colombier /* Load a 1-Bit encoded BMP file (uncompressed) */
79fcdc259eSDavid du Colombier static int
load_1T(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)80fcdc259eSDavid du Colombier load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
81fcdc259eSDavid du Colombier {
82fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32;
83fcdc259eSDavid du Colombier 	int val = 0, n;
84fcdc259eSDavid du Colombier 
85fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
86fcdc259eSDavid du Colombier 		i = (height - 1) * width;
87fcdc259eSDavid du Colombier 		step_up = -2 * width;
88fcdc259eSDavid du Colombier 	} else
89fcdc259eSDavid du Colombier 		height = -height;
90fcdc259eSDavid du Colombier 
91fcdc259eSDavid du Colombier 	for(iy = height; iy; iy--, i += step_up)
92fcdc259eSDavid du Colombier 		for(ix = 0, n = 0; ix < padded_width; ix++, n--) {
93fcdc259eSDavid du Colombier 			if(!n) {
94fcdc259eSDavid du Colombier 				val = Bgetc(b);
95fcdc259eSDavid du Colombier 				n = 8;
96fcdc259eSDavid du Colombier 			}
97fcdc259eSDavid du Colombier 			if(ix < width) {
98fcdc259eSDavid du Colombier 				buf[i] = clut[val & 0x80 ? 1 : 0];
99fcdc259eSDavid du Colombier 				i++;
100fcdc259eSDavid du Colombier 			}
101fcdc259eSDavid du Colombier 			val <<= 1;
102fcdc259eSDavid du Colombier 		}
103fcdc259eSDavid du Colombier 	return 0;
104fcdc259eSDavid du Colombier }
105fcdc259eSDavid du Colombier 
106fcdc259eSDavid du Colombier /* Load a 4-Bit encoded BMP file (uncompressed) */
107fcdc259eSDavid du Colombier static int
load_4T(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)108fcdc259eSDavid du Colombier load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut)
109fcdc259eSDavid du Colombier {
110fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3;
111fcdc259eSDavid du Colombier 	uint valH, valL;
112fcdc259eSDavid du Colombier 
113fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
114fcdc259eSDavid du Colombier 		i = (height - 1) * width;
115fcdc259eSDavid du Colombier 		step_up = -2 * width;
116fcdc259eSDavid du Colombier 	} else
117fcdc259eSDavid du Colombier 		height = -height;
118fcdc259eSDavid du Colombier 
119fcdc259eSDavid du Colombier 	for(iy = height; iy; iy--, i += step_up) {
120fcdc259eSDavid du Colombier 		for(ix = 0; ix < width; ) {
121fcdc259eSDavid du Colombier 			valH = valL = Bgetc(b) & 0xff;
122fcdc259eSDavid du Colombier 			valH >>= 4;
123fcdc259eSDavid du Colombier 
124fcdc259eSDavid du Colombier 			buf[i] = clut[valH];
125fcdc259eSDavid du Colombier 			i++; ix++;
126fcdc259eSDavid du Colombier 
127fcdc259eSDavid du Colombier 			if(ix < width) {
128fcdc259eSDavid du Colombier 				valL &= 0xf;
129fcdc259eSDavid du Colombier 				buf[i] = clut[valL];
130fcdc259eSDavid du Colombier 				i++; ix++;
131fcdc259eSDavid du Colombier 			}
132fcdc259eSDavid du Colombier 		}
133fcdc259eSDavid du Colombier 		Bseek(b, skip, 1);
134fcdc259eSDavid du Colombier 	}
135fcdc259eSDavid du Colombier 	return 0;
136fcdc259eSDavid du Colombier }
137fcdc259eSDavid du Colombier 
138fcdc259eSDavid du Colombier /* Load a 4-Bit encoded BMP file (RLE4-compressed) */
139fcdc259eSDavid du Colombier static int
load_4C(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)140fcdc259eSDavid du Colombier load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
141fcdc259eSDavid du Colombier {
142fcdc259eSDavid du Colombier 	long ix, iy = height -1;
143fcdc259eSDavid du Colombier 	uint val, valS, skip;
144fcdc259eSDavid du Colombier 	Rgb* p;
145fcdc259eSDavid du Colombier 
146fcdc259eSDavid du Colombier 	while(iy >= 0) {
147fcdc259eSDavid du Colombier 		ix = 0;
148fcdc259eSDavid du Colombier 		while(ix < width) {
149*c93608ccSDavid du Colombier 			val = (uint)Bgetc(b);
150fcdc259eSDavid du Colombier 
151fcdc259eSDavid du Colombier 			if(0 != val) {
152fcdc259eSDavid du Colombier 				valS = (uint)Bgetc(b);
153fcdc259eSDavid du Colombier 				p = &buf[ix + iy * width];
154fcdc259eSDavid du Colombier 				while(val--) {
155fcdc259eSDavid du Colombier 					*p = clut[0xf & (valS >> 4)];
156fcdc259eSDavid du Colombier 					p++;
157fcdc259eSDavid du Colombier 					ix++;
158*c93608ccSDavid du Colombier 					if(val != 0) {
159fcdc259eSDavid du Colombier 						*p = clut[0xf & valS];
160fcdc259eSDavid du Colombier 						p++;
161fcdc259eSDavid du Colombier 						ix++;
162fcdc259eSDavid du Colombier 						val--;
163fcdc259eSDavid du Colombier 					}
164fcdc259eSDavid du Colombier 				}
165fcdc259eSDavid du Colombier 			} else {
166fcdc259eSDavid du Colombier 				/* Special modes... */
167fcdc259eSDavid du Colombier 				val = Bgetc(b);
168fcdc259eSDavid du Colombier 				switch(val) {
169fcdc259eSDavid du Colombier 					case 0:	/* End-Of-Line detected */
170fcdc259eSDavid du Colombier 						ix = width;
171fcdc259eSDavid du Colombier 						iy--;
172fcdc259eSDavid du Colombier 						break;
173fcdc259eSDavid du Colombier 					case 1:	/* End-Of-Picture detected -->> abort */
174fcdc259eSDavid du Colombier 						ix = width;
175fcdc259eSDavid du Colombier 						iy = -1;
176fcdc259eSDavid du Colombier 						break;
177fcdc259eSDavid du Colombier 					case 2:	/* Position change detected */
178*c93608ccSDavid du Colombier 						val = (uint)Bgetc(b);
179fcdc259eSDavid du Colombier 						ix += val;
180*c93608ccSDavid du Colombier 						val = (uint)Bgetc(b);
181fcdc259eSDavid du Colombier 						iy -= val;
182fcdc259eSDavid du Colombier 						break;
183fcdc259eSDavid du Colombier 
184fcdc259eSDavid du Colombier 					default:/* Transparent data sequence detected */
185fcdc259eSDavid du Colombier 						p = &buf[ix + iy * width];
186fcdc259eSDavid du Colombier 						if((1 == (val & 3)) || (2 == (val & 3)))
187fcdc259eSDavid du Colombier 							skip = 1;
188fcdc259eSDavid du Colombier 						else
189fcdc259eSDavid du Colombier 							skip = 0;
190fcdc259eSDavid du Colombier 
191fcdc259eSDavid du Colombier 						while(val--) {
192fcdc259eSDavid du Colombier 							valS = (uint)Bgetc(b);
193fcdc259eSDavid du Colombier 							*p = clut[0xf & (valS >> 4)];
194fcdc259eSDavid du Colombier 							p++;
195fcdc259eSDavid du Colombier 							ix++;
196*c93608ccSDavid du Colombier 							if(val != 0) {
197fcdc259eSDavid du Colombier 								*p = clut[0xf & valS];
198fcdc259eSDavid du Colombier 								p++;
199fcdc259eSDavid du Colombier 								ix++;
200fcdc259eSDavid du Colombier 								val--;
201fcdc259eSDavid du Colombier 							}
202fcdc259eSDavid du Colombier 						}
203fcdc259eSDavid du Colombier 						if(skip)
204fcdc259eSDavid du Colombier 							Bgetc(b);
205fcdc259eSDavid du Colombier 						break;
206fcdc259eSDavid du Colombier 				}
207fcdc259eSDavid du Colombier 			}
208fcdc259eSDavid du Colombier 		}
209fcdc259eSDavid du Colombier 	}
210fcdc259eSDavid du Colombier 	return 0;
211fcdc259eSDavid du Colombier }
212fcdc259eSDavid du Colombier 
213fcdc259eSDavid du Colombier /* Load a 8-Bit encoded BMP file (uncompressed) */
214fcdc259eSDavid du Colombier static int
load_8T(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)215fcdc259eSDavid du Colombier load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
216fcdc259eSDavid du Colombier {
217fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3;
218fcdc259eSDavid du Colombier 
219fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
220fcdc259eSDavid du Colombier 		i = (height - 1) * width;
221fcdc259eSDavid du Colombier 		step_up = -2 * width;
222fcdc259eSDavid du Colombier 	} else
223fcdc259eSDavid du Colombier 		height = -height;
224fcdc259eSDavid du Colombier 
225fcdc259eSDavid du Colombier 	for(iy = height; iy; iy--, i += step_up) {
226fcdc259eSDavid du Colombier 		for(ix = 0; ix < width; ix++, i++)
227fcdc259eSDavid du Colombier 			buf[i] = clut[Bgetc(b) & 0xff];
228fcdc259eSDavid du Colombier 		Bseek(b, skip, 1);
229fcdc259eSDavid du Colombier 	}
230fcdc259eSDavid du Colombier 	return 0;
231fcdc259eSDavid du Colombier }
232fcdc259eSDavid du Colombier 
233fcdc259eSDavid du Colombier /* Load a 8-Bit encoded BMP file (RLE8-compressed) */
234fcdc259eSDavid du Colombier static int
load_8C(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)235fcdc259eSDavid du Colombier load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
236fcdc259eSDavid du Colombier {
237fcdc259eSDavid du Colombier 	long ix, iy = height -1;
238fcdc259eSDavid du Colombier 	int val, valS, skip;
239fcdc259eSDavid du Colombier 	Rgb* p;
240fcdc259eSDavid du Colombier 
241fcdc259eSDavid du Colombier 	while(iy >= 0) {
242fcdc259eSDavid du Colombier 		ix = 0;
243fcdc259eSDavid du Colombier 		while(ix < width) {
244fcdc259eSDavid du Colombier 			val = Bgetc(b);
245fcdc259eSDavid du Colombier 
246fcdc259eSDavid du Colombier 			if(0 != val) {
247fcdc259eSDavid du Colombier 				valS = Bgetc(b);
248fcdc259eSDavid du Colombier 				p = &buf[ix + iy * width];
249fcdc259eSDavid du Colombier 				while(val--) {
250fcdc259eSDavid du Colombier 					*p = clut[valS];
251fcdc259eSDavid du Colombier 					p++;
252fcdc259eSDavid du Colombier 					ix++;
253fcdc259eSDavid du Colombier 				}
254fcdc259eSDavid du Colombier 			} else {
255fcdc259eSDavid du Colombier 				/* Special modes... */
256fcdc259eSDavid du Colombier 				val = Bgetc(b);
257fcdc259eSDavid du Colombier 				switch(val) {
258fcdc259eSDavid du Colombier 					case 0: /* End-Of-Line detected */
259fcdc259eSDavid du Colombier 						ix = width;
260fcdc259eSDavid du Colombier 						iy--;
261fcdc259eSDavid du Colombier 						break;
262fcdc259eSDavid du Colombier 					case 1: /* End-Of-Picture detected */
263fcdc259eSDavid du Colombier 						ix = width;
264fcdc259eSDavid du Colombier 						iy = -1;
265fcdc259eSDavid du Colombier 						break;
266fcdc259eSDavid du Colombier 					case 2: /* Position change detected */
267fcdc259eSDavid du Colombier 						val = Bgetc(b);
268fcdc259eSDavid du Colombier 						ix += val;
269fcdc259eSDavid du Colombier 						val = Bgetc(b);
270fcdc259eSDavid du Colombier 						iy -= val;
271fcdc259eSDavid du Colombier 						break;
272fcdc259eSDavid du Colombier 					default: /* Transparent (not compressed) sequence detected */
273fcdc259eSDavid du Colombier 						p = &buf[ix + iy * width];
274fcdc259eSDavid du Colombier 						if(val & 1)
275fcdc259eSDavid du Colombier 							skip = 1;
276fcdc259eSDavid du Colombier 						else
277fcdc259eSDavid du Colombier 							skip = 0;
278fcdc259eSDavid du Colombier 
279fcdc259eSDavid du Colombier 						while(val--) {
280fcdc259eSDavid du Colombier 							valS = Bgetc(b);
281fcdc259eSDavid du Colombier 							*p = clut[valS];
282fcdc259eSDavid du Colombier 							p++;
283fcdc259eSDavid du Colombier 							ix++;
284fcdc259eSDavid du Colombier 						}
285fcdc259eSDavid du Colombier 						if(skip)
286fcdc259eSDavid du Colombier 							/* Align data stream */
287fcdc259eSDavid du Colombier 							Bgetc(b);
288fcdc259eSDavid du Colombier 						break;
289fcdc259eSDavid du Colombier 				}
290fcdc259eSDavid du Colombier 			}
291fcdc259eSDavid du Colombier 		}
292fcdc259eSDavid du Colombier 	}
293fcdc259eSDavid du Colombier 	return 0;
294fcdc259eSDavid du Colombier }
295fcdc259eSDavid du Colombier 
296fcdc259eSDavid du Colombier /* Load a 16-Bit encoded BMP file (uncompressed) */
297fcdc259eSDavid du Colombier static int
load_16(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)298fcdc259eSDavid du Colombier load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
299fcdc259eSDavid du Colombier {
300fcdc259eSDavid du Colombier 	uchar c[2];
301fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0;
302fcdc259eSDavid du Colombier 
303fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
304fcdc259eSDavid du Colombier 		i = (height - 1) * width;
305fcdc259eSDavid du Colombier 		step_up = -2 * width;
306fcdc259eSDavid du Colombier 	} else
307fcdc259eSDavid du Colombier 		height = -height;
308fcdc259eSDavid du Colombier 
309fcdc259eSDavid du Colombier 	if(clut) {
310fcdc259eSDavid du Colombier 		unsigned mask_blue =  (unsigned)clut[0].blue +
311fcdc259eSDavid du Colombier 		                     ((unsigned)clut[0].green << 8);
312fcdc259eSDavid du Colombier 		unsigned mask_green =  (unsigned)clut[1].blue +
313fcdc259eSDavid du Colombier 		                      ((unsigned)clut[1].green << 8);
314fcdc259eSDavid du Colombier 		unsigned mask_red =  (unsigned)clut[2].blue +
315fcdc259eSDavid du Colombier 		                    ((unsigned)clut[2].green << 8);
316fcdc259eSDavid du Colombier 		int shft_blue = msb((ulong)mask_blue) - 8;
317fcdc259eSDavid du Colombier 		int shft_green = msb((ulong)mask_green) - 8;
318fcdc259eSDavid du Colombier 		int shft_red = msb((ulong)mask_red) - 8;
319fcdc259eSDavid du Colombier 
320fcdc259eSDavid du Colombier 		for(iy = height; iy; iy--, i += step_up)
321fcdc259eSDavid du Colombier 			for(ix = 0; ix < width; ix++, i++) {
322fcdc259eSDavid du Colombier 				unsigned val;
323fcdc259eSDavid du Colombier 				Bread(b, c, sizeof(c));
324fcdc259eSDavid du Colombier 				val = (unsigned)c[0] + ((unsigned)c[1] << 8);
325fcdc259eSDavid du Colombier 
326fcdc259eSDavid du Colombier 				buf[i].alpha = 0;
327fcdc259eSDavid du Colombier 				if(shft_blue >= 0)
328fcdc259eSDavid du Colombier 					buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
329fcdc259eSDavid du Colombier 				else
330fcdc259eSDavid du Colombier 					buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
331fcdc259eSDavid du Colombier 				if(shft_green >= 0)
332fcdc259eSDavid du Colombier 					buf[i].green = (uchar)((val & mask_green) >> shft_green);
333fcdc259eSDavid du Colombier 				else
334fcdc259eSDavid du Colombier 					buf[i].green = (uchar)((val & mask_green) << -shft_green);
335fcdc259eSDavid du Colombier 				if(shft_red >= 0)
336fcdc259eSDavid du Colombier 					buf[i].red = (uchar)((val & mask_red) >> shft_red);
337fcdc259eSDavid du Colombier 				else
338fcdc259eSDavid du Colombier 					buf[i].red = (uchar)((val & mask_red) << -shft_red);
339fcdc259eSDavid du Colombier 			}
340fcdc259eSDavid du Colombier 	} else
341fcdc259eSDavid du Colombier 		for(iy = height; iy; iy--, i += step_up)
342fcdc259eSDavid du Colombier 			for(ix = 0; ix < width; ix++, i++) {
343fcdc259eSDavid du Colombier 				Bread(b, c, sizeof(c));
344fcdc259eSDavid du Colombier 				buf[i].blue = (uchar)((c[0] << 3) & 0xf8);
345fcdc259eSDavid du Colombier 				buf[i].green = (uchar)(((((unsigned)c[1] << 6) +
346fcdc259eSDavid du Colombier 				                        (((unsigned)c[0]) >> 2))) & 0xf8);
347fcdc259eSDavid du Colombier 				buf[i].red = (uchar)((c[1] << 1) & 0xf8);
348fcdc259eSDavid du Colombier 			}
349fcdc259eSDavid du Colombier 	return 0;
350fcdc259eSDavid du Colombier }
351fcdc259eSDavid du Colombier 
352fcdc259eSDavid du Colombier /* Load a 24-Bit encoded BMP file (uncompressed) */
353fcdc259eSDavid du Colombier static int
load_24T(Biobuf * b,long width,long height,Rgb * buf)354fcdc259eSDavid du Colombier load_24T(Biobuf* b, long width, long height, Rgb* buf)
355fcdc259eSDavid du Colombier {
356fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3;
357fcdc259eSDavid du Colombier 
358fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
359fcdc259eSDavid du Colombier 		i = (height - 1) * width;
360fcdc259eSDavid du Colombier 		step_up = -2 * width;
361fcdc259eSDavid du Colombier 	} else
362fcdc259eSDavid du Colombier 		height = -height;
363fcdc259eSDavid du Colombier 
364fcdc259eSDavid du Colombier 	for(iy = height; iy; iy--, i += step_up) {
365fcdc259eSDavid du Colombier 		for(ix = 0; ix < width; ix++, i++) {
366fcdc259eSDavid du Colombier 			buf[i].alpha = 0;
367fcdc259eSDavid du Colombier 			buf[i].blue = Bgetc(b);
368fcdc259eSDavid du Colombier 			buf[i].green = Bgetc(b);
369fcdc259eSDavid du Colombier 			buf[i].red = Bgetc(b);
370fcdc259eSDavid du Colombier 		}
371fcdc259eSDavid du Colombier 		Bseek(b, skip, 1);
372fcdc259eSDavid du Colombier 	}
373fcdc259eSDavid du Colombier 	return 0;
374fcdc259eSDavid du Colombier }
375fcdc259eSDavid du Colombier 
376fcdc259eSDavid du Colombier /* Load a 32-Bit encoded BMP file (uncompressed) */
377fcdc259eSDavid du Colombier static int
load_32(Biobuf * b,long width,long height,Rgb * buf,Rgb * clut)378fcdc259eSDavid du Colombier load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
379fcdc259eSDavid du Colombier {
380fcdc259eSDavid du Colombier 	uchar c[4];
381fcdc259eSDavid du Colombier 	long ix, iy, i = 0, step_up = 0;
382fcdc259eSDavid du Colombier 
383fcdc259eSDavid du Colombier 	if(height > 0) {	/* bottom-up */
384fcdc259eSDavid du Colombier 		i = (height - 1) * width;
385fcdc259eSDavid du Colombier 		step_up = -2 * width;
386fcdc259eSDavid du Colombier 	} else
387fcdc259eSDavid du Colombier 		height = -height;
388fcdc259eSDavid du Colombier 
389fcdc259eSDavid du Colombier 	if(clut) {
390fcdc259eSDavid du Colombier 		ulong mask_blue =  (ulong)clut[0].blue +
391fcdc259eSDavid du Colombier 		                          ((ulong)clut[0].green << 8) +
392fcdc259eSDavid du Colombier 		                          ((ulong)clut[0].red << 16) +
393fcdc259eSDavid du Colombier 		                          ((ulong)clut[0].alpha << 24);
394fcdc259eSDavid du Colombier 		ulong mask_green =  (ulong)clut[1].blue +
395fcdc259eSDavid du Colombier 		                           ((ulong)clut[1].green << 8) +
396fcdc259eSDavid du Colombier 		                           ((ulong)clut[1].red << 16) +
397fcdc259eSDavid du Colombier 		                           ((ulong)clut[1].alpha << 24);
398fcdc259eSDavid du Colombier 		ulong mask_red =  (ulong)clut[2].blue +
399fcdc259eSDavid du Colombier 		                         ((ulong)clut[2].green << 8) +
400fcdc259eSDavid du Colombier 		                         ((ulong)clut[2].red << 16) +
401fcdc259eSDavid du Colombier 		                         ((ulong)clut[2].alpha << 24);
402fcdc259eSDavid du Colombier 		int shft_blue = msb(mask_blue) - 8;
403fcdc259eSDavid du Colombier 		int shft_green = msb(mask_green) - 8;
404fcdc259eSDavid du Colombier 		int shft_red = msb(mask_red) - 8;
405fcdc259eSDavid du Colombier 
406fcdc259eSDavid du Colombier 		for(iy = height; iy; iy--, i += step_up)
407fcdc259eSDavid du Colombier 			for(ix = 0; ix < width; ix++, i++) {
408fcdc259eSDavid du Colombier 				ulong val;
409fcdc259eSDavid du Colombier 				Bread(b, c, sizeof(c));
410fcdc259eSDavid du Colombier 				val =  (ulong)c[0] + ((ulong)c[1] << 8) +
411fcdc259eSDavid du Colombier 				      ((ulong)c[2] << 16) + ((ulong)c[1] << 24);
412fcdc259eSDavid du Colombier 
413fcdc259eSDavid du Colombier 				buf[i].alpha = 0;
414fcdc259eSDavid du Colombier 				if(shft_blue >= 0)
415fcdc259eSDavid du Colombier 					buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
416fcdc259eSDavid du Colombier 				else
417fcdc259eSDavid du Colombier 					buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
418fcdc259eSDavid du Colombier 				if(shft_green >= 0)
419fcdc259eSDavid du Colombier 					buf[i].green = (uchar)((val & mask_green) >> shft_green);
420fcdc259eSDavid du Colombier 				else
421fcdc259eSDavid du Colombier 					buf[i].green = (uchar)((val & mask_green) << -shft_green);
422fcdc259eSDavid du Colombier 				if(shft_red >= 0)
423fcdc259eSDavid du Colombier 					buf[i].red = (uchar)((val & mask_red) >> shft_red);
424fcdc259eSDavid du Colombier 				else
425fcdc259eSDavid du Colombier 					buf[i].red = (uchar)((val & mask_red) << -shft_red);
426fcdc259eSDavid du Colombier 			}
427fcdc259eSDavid du Colombier 	} else
428fcdc259eSDavid du Colombier 		for(iy = height; iy; iy--, i += step_up)
429fcdc259eSDavid du Colombier 			for(ix = 0; ix < width; ix++, i++) {
430fcdc259eSDavid du Colombier 				Bread(b, c, nelem(c));
431fcdc259eSDavid du Colombier 				buf[i].blue = c[0];
432fcdc259eSDavid du Colombier 				buf[i].green = c[1];
433fcdc259eSDavid du Colombier 				buf[i].red = c[2];
434fcdc259eSDavid du Colombier 			}
435fcdc259eSDavid du Colombier 	return 0;
436fcdc259eSDavid du Colombier }
437fcdc259eSDavid du Colombier 
438fcdc259eSDavid du Colombier 
439fcdc259eSDavid du Colombier static Rgb*
ReadBMP(Biobuf * b,int * width,int * height)440fcdc259eSDavid du Colombier ReadBMP(Biobuf *b, int *width, int *height)
441fcdc259eSDavid du Colombier {
442fcdc259eSDavid du Colombier 	int colours, num_coltab = 0;
443fcdc259eSDavid du Colombier 	Filehdr bmfh;
444fcdc259eSDavid du Colombier 	Infohdr bmih;
445fcdc259eSDavid du Colombier 	Rgb clut[256];
446fcdc259eSDavid du Colombier 	Rgb* buf;
447fcdc259eSDavid du Colombier 
448fcdc259eSDavid du Colombier 	bmfh.type = r16(b);
449fcdc259eSDavid du Colombier 	if(bmfh.type != 0x4d42) 	/* signature must be 'BM' */
450fcdc259eSDavid du Colombier 		sysfatal("bad magic number, not a BMP file");
451fcdc259eSDavid du Colombier 
452fcdc259eSDavid du Colombier 	bmfh.size = r32(b);
453fcdc259eSDavid du Colombier 	bmfh.reserved1 = r16(b);
454fcdc259eSDavid du Colombier 	bmfh.reserved2 = r16(b);
455fcdc259eSDavid du Colombier 	bmfh.offbits = r32(b);
456fcdc259eSDavid du Colombier 
457fcdc259eSDavid du Colombier 	memset(&bmih, 0, sizeof(bmih));
458fcdc259eSDavid du Colombier 	bmih.size = r32(b);
459fcdc259eSDavid du Colombier 
460fcdc259eSDavid du Colombier 	if(bmih.size == 0x0c) {			/* OS/2 1.x version */
461fcdc259eSDavid du Colombier 		bmih.width = r16(b);
462fcdc259eSDavid du Colombier 		bmih.height = r16(b);
463fcdc259eSDavid du Colombier 		bmih.planes = r16(b);
464fcdc259eSDavid du Colombier 		bmih.bpp = r16(b);
465fcdc259eSDavid du Colombier 		bmih.compression = BMP_RGB;
466fcdc259eSDavid du Colombier 	} else {				/* Windows */
467fcdc259eSDavid du Colombier 		bmih.width = r32(b);
468fcdc259eSDavid du Colombier 		bmih.height = r32(b);
469fcdc259eSDavid du Colombier 		bmih.planes = r16(b);
470fcdc259eSDavid du Colombier 		bmih.bpp = r16(b);
471fcdc259eSDavid du Colombier 		bmih.compression = r32(b);
472fcdc259eSDavid du Colombier 		bmih.imagesize = r32(b);
473fcdc259eSDavid du Colombier 		bmih.hres = r32(b);
474fcdc259eSDavid du Colombier 		bmih.vres = r32(b);
475fcdc259eSDavid du Colombier 		bmih.colours = r32(b);
476fcdc259eSDavid du Colombier 		bmih.impcolours = r32(b);
477fcdc259eSDavid du Colombier 	}
478fcdc259eSDavid du Colombier 
479fcdc259eSDavid du Colombier 	if(bmih.bpp < 16) {
480fcdc259eSDavid du Colombier 		/* load colour table */
481fcdc259eSDavid du Colombier 		if(bmih.impcolours)
482fcdc259eSDavid du Colombier 			num_coltab = (int)bmih.impcolours;
483fcdc259eSDavid du Colombier 		else
484fcdc259eSDavid du Colombier 			num_coltab = 1 << bmih.bpp;
485fcdc259eSDavid du Colombier 	} else if(bmih.compression == BMP_BITFIELDS &&
486fcdc259eSDavid du Colombier 	          (bmih.bpp == 16 || bmih.bpp == 32))
487fcdc259eSDavid du Colombier 		/* load bitmasks */
488fcdc259eSDavid du Colombier 		num_coltab = 3;
489fcdc259eSDavid du Colombier 
490fcdc259eSDavid du Colombier 	if(num_coltab) {
491fcdc259eSDavid du Colombier 		int i;
492fbbcd4dcSDavid du Colombier 		Bseek(b, bmih.size + Filehdrsz, 0);
493fcdc259eSDavid du Colombier 
494fcdc259eSDavid du Colombier 		for(i = 0; i < num_coltab; i++) {
495fcdc259eSDavid du Colombier 			clut[i].blue  = (uchar)Bgetc(b);
496fcdc259eSDavid du Colombier 			clut[i].green = (uchar)Bgetc(b);
497fcdc259eSDavid du Colombier 			clut[i].red   = (uchar)Bgetc(b);
498fcdc259eSDavid du Colombier 			clut[i].alpha = (uchar)Bgetc(b);
499fcdc259eSDavid du Colombier 		}
500fcdc259eSDavid du Colombier 	}
501fcdc259eSDavid du Colombier 
502fcdc259eSDavid du Colombier 	*width = bmih.width;
503fcdc259eSDavid du Colombier 	*height = bmih.height;
504fcdc259eSDavid du Colombier 	colours = bmih.bpp;
505fcdc259eSDavid du Colombier 
506fcdc259eSDavid du Colombier 	Bseek(b, bmfh.offbits, 0);
507fcdc259eSDavid du Colombier 
508fcdc259eSDavid du Colombier 	if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil)
509fcdc259eSDavid du Colombier 		sysfatal("no memory");
510fcdc259eSDavid du Colombier 
511fcdc259eSDavid du Colombier 	switch(colours) {
512fcdc259eSDavid du Colombier 		case 1:
513fcdc259eSDavid du Colombier 			load_1T(b, *width, *height, buf, clut);
514fcdc259eSDavid du Colombier 			break;
515fcdc259eSDavid du Colombier 		case 4:
516fcdc259eSDavid du Colombier 			if(bmih.compression == BMP_RLE4)
517fcdc259eSDavid du Colombier 				load_4C(b, *width, *height, buf, clut);
518fcdc259eSDavid du Colombier 			else
519fcdc259eSDavid du Colombier 				load_4T(b, *width, *height, buf, clut);
520fcdc259eSDavid du Colombier 			break;
521fcdc259eSDavid du Colombier 		case 8:
522fcdc259eSDavid du Colombier 			if(bmih.compression == BMP_RLE8)
523fcdc259eSDavid du Colombier 				load_8C(b, *width, *height, buf, clut);
524fcdc259eSDavid du Colombier 			else
525fcdc259eSDavid du Colombier 				load_8T(b, *width, *height, buf, clut);
526fcdc259eSDavid du Colombier 			break;
527fcdc259eSDavid du Colombier 		case 16:
528fcdc259eSDavid du Colombier 			load_16(b, *width, *height, buf,
529fcdc259eSDavid du Colombier 			        bmih.compression == BMP_BITFIELDS ? clut : nil);
530fcdc259eSDavid du Colombier 			break;
531fcdc259eSDavid du Colombier 		case 24:
532fcdc259eSDavid du Colombier 			load_24T(b, *width, *height, buf);
533fcdc259eSDavid du Colombier 			break;
534fcdc259eSDavid du Colombier 		case 32:
535fcdc259eSDavid du Colombier 			load_32(b, *width, *height, buf,
536fcdc259eSDavid du Colombier 			        bmih.compression == BMP_BITFIELDS ? clut : nil);
537fcdc259eSDavid du Colombier 			break;
538fcdc259eSDavid du Colombier 	}
539fcdc259eSDavid du Colombier 	return buf;
540fcdc259eSDavid du Colombier }
541fcdc259eSDavid du Colombier 
542fcdc259eSDavid du Colombier Rawimage**
Breadbmp(Biobuf * bp,int colourspace)543fcdc259eSDavid du Colombier Breadbmp(Biobuf *bp, int colourspace)
544fcdc259eSDavid du Colombier {
545fcdc259eSDavid du Colombier 	Rawimage *a, **array;
546fcdc259eSDavid du Colombier 	int c, width, height;
547fcdc259eSDavid du Colombier 	uchar *r, *g, *b;
548fcdc259eSDavid du Colombier 	Rgb *s, *e;
549fcdc259eSDavid du Colombier 	Rgb *bmp;
550fcdc259eSDavid du Colombier 	char ebuf[128];
551fcdc259eSDavid du Colombier 
552fcdc259eSDavid du Colombier 	a = nil;
553fcdc259eSDavid du Colombier 	bmp = nil;
554fcdc259eSDavid du Colombier 	array = nil;
555fcdc259eSDavid du Colombier 	USED(a);
556fcdc259eSDavid du Colombier 	USED(bmp);
557fcdc259eSDavid du Colombier 	if (colourspace != CRGB) {
558fcdc259eSDavid du Colombier 		errstr(ebuf, sizeof ebuf);	/* throw it away */
559fcdc259eSDavid du Colombier 		werrstr("ReadRGB: unknown colour space %d", colourspace);
560fcdc259eSDavid du Colombier 		return nil;
561fcdc259eSDavid du Colombier 	}
562fcdc259eSDavid du Colombier 
563fcdc259eSDavid du Colombier 	if ((bmp = ReadBMP(bp, &width, &height)) == nil)
564fcdc259eSDavid du Colombier 		return nil;
565fcdc259eSDavid du Colombier 
566fcdc259eSDavid du Colombier 	if ((a = calloc(sizeof(Rawimage), 1)) == nil)
567fcdc259eSDavid du Colombier 		goto Error;
568fcdc259eSDavid du Colombier 
569fcdc259eSDavid du Colombier 	for (c = 0; c  < 3; c++)
570fcdc259eSDavid du Colombier 		if ((a->chans[c] = calloc(width, height)) == nil)
571fcdc259eSDavid du Colombier 			goto Error;
572fcdc259eSDavid du Colombier 
573fcdc259eSDavid du Colombier 	if ((array = calloc(sizeof(Rawimage *), 2)) == nil)
574fcdc259eSDavid du Colombier 		goto Error;
575fcdc259eSDavid du Colombier 	array[0] = a;
576fcdc259eSDavid du Colombier 	array[1] = nil;
577fcdc259eSDavid du Colombier 
578fcdc259eSDavid du Colombier 	a->nchans = 3;
579fcdc259eSDavid du Colombier 	a->chandesc = CRGB;
580fcdc259eSDavid du Colombier 	a->chanlen = width * height;
581fcdc259eSDavid du Colombier 	a->r = Rect(0, 0, width, height);
582fcdc259eSDavid du Colombier 
583fcdc259eSDavid du Colombier 	s = bmp;
584fcdc259eSDavid du Colombier 	e = s + width * height;
585fcdc259eSDavid du Colombier 	r = a->chans[0];
586fcdc259eSDavid du Colombier 	g = a->chans[1];
587fcdc259eSDavid du Colombier 	b = a->chans[2];
588fcdc259eSDavid du Colombier 
589fcdc259eSDavid du Colombier 	do {
590fcdc259eSDavid du Colombier 		*r++ = s->red;
591fcdc259eSDavid du Colombier 		*g++ = s->green;
592fcdc259eSDavid du Colombier 		*b++ = s->blue;
593fcdc259eSDavid du Colombier 	}while(++s < e);
594fcdc259eSDavid du Colombier 
595fcdc259eSDavid du Colombier 	free(bmp);
596fcdc259eSDavid du Colombier 	return array;
597fcdc259eSDavid du Colombier 
598fcdc259eSDavid du Colombier Error:
599fcdc259eSDavid du Colombier 	if (a)
600fcdc259eSDavid du Colombier 		for (c = 0; c < 3; c++)
601fcdc259eSDavid du Colombier 			if (a->chans[c])
602fcdc259eSDavid du Colombier 				free(a->chans[c]);
603fcdc259eSDavid du Colombier 	if (a)
604fcdc259eSDavid du Colombier 		free(a);
605fcdc259eSDavid du Colombier 	if (array)
606fcdc259eSDavid du Colombier 		free(array);
607fcdc259eSDavid du Colombier 	if (bmp)
608fcdc259eSDavid du Colombier 		free(bmp);
609fcdc259eSDavid du Colombier 	return nil;
610fcdc259eSDavid du Colombier 
611fcdc259eSDavid du Colombier }
612fcdc259eSDavid du Colombier 
613fcdc259eSDavid du Colombier Rawimage**
readbmp(int fd,int colorspace)614fcdc259eSDavid du Colombier readbmp(int fd, int colorspace)
615fcdc259eSDavid du Colombier {
616fcdc259eSDavid du Colombier 	Rawimage * *a;
617fcdc259eSDavid du Colombier 	Biobuf b;
618fcdc259eSDavid du Colombier 
619fcdc259eSDavid du Colombier 	if (Binit(&b, fd, OREAD) < 0)
620fcdc259eSDavid du Colombier 		return nil;
621fcdc259eSDavid du Colombier 	a = Breadbmp(&b, colorspace);
622fcdc259eSDavid du Colombier 	Bterm(&b);
623fcdc259eSDavid du Colombier 	return a;
624fcdc259eSDavid du Colombier }
625fcdc259eSDavid du Colombier 
626fcdc259eSDavid du Colombier 
627