Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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