Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include "imagefile.h"
6 #include "bmp.h"
8 /*
9 MS-BMP file reader
10 (c) 2003, I.P.Keller
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:
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
36 OS/2 1.x bmp files are recognised as well, but testing was very limited.
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 */
43 static short
44 r16(Biobuf*b)
45 {
46 short s;
48 s = Bgetc(b);
49 s |= ((short)Bgetc(b)) << 8;
50 return s;
51 }
54 static long
55 r32(Biobuf*b)
56 {
57 long l;
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 }
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 }
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;
85 if(height > 0) { /* bottom-up */
86 i = (height - 1) * width;
87 step_up = -2 * width;
88 } else
89 height = -height;
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++;
101 val <<= 1;
103 return 0;
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)
110 long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3;
111 uint valH, valL;
113 if(height > 0) { /* bottom-up */
114 i = (height - 1) * width;
115 step_up = -2 * width;
116 } else
117 height = -height;
119 for(iy = height; iy; iy--, i += step_up) {
120 for(ix = 0; ix < width; ) {
121 valH = valL = Bgetc(b) & 0xff;
122 valH >>= 4;
124 buf[i] = clut[valH];
125 i++; ix++;
127 if(ix < width) {
128 valL &= 0xf;
129 buf[i] = clut[valL];
130 i++; ix++;
133 Bseek(b, skip, 1);
135 return 0;
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)
142 long ix, iy = height -1;
143 uint val, valS, skip;
144 Rgb* p;
146 while(iy >= 0) {
147 ix = 0;
148 while(ix < width) {
149 val = Bgetc(b);
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(0 < val) {
159 *p = clut[0xf & valS];
160 p++;
161 ix++;
162 val--;
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 = Bgetc(b);
179 ix += val;
180 val = Bgetc(b);
181 iy -= val;
182 break;
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;
191 while(val--) {
192 valS = (uint)Bgetc(b);
193 *p = clut[0xf & (valS >> 4)];
194 p++;
195 ix++;
196 if(0 < val) {
197 *p = clut[0xf & valS];
198 p++;
199 ix++;
200 val--;
203 if(skip)
204 Bgetc(b);
205 break;
210 return 0;
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)
217 long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3;
219 if(height > 0) { /* bottom-up */
220 i = (height - 1) * width;
221 step_up = -2 * width;
222 } else
223 height = -height;
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);
230 return 0;
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)
237 long ix, iy = height -1;
238 int val, valS, skip;
239 Rgb* p;
241 while(iy >= 0) {
242 ix = 0;
243 while(ix < width) {
244 val = Bgetc(b);
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++;
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;
279 while(val--) {
280 valS = Bgetc(b);
281 *p = clut[valS];
282 p++;
283 ix++;
285 if(skip)
286 /* Align data stream */
287 Bgetc(b);
288 break;
293 return 0;
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)
300 uchar c[2];
301 long ix, iy, i = 0, step_up = 0;
303 if(height > 0) { /* bottom-up */
304 i = (height - 1) * width;
305 step_up = -2 * width;
306 } else
307 height = -height;
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;
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);
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);
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);
349 return 0;
352 /* Load a 24-Bit encoded BMP file (uncompressed) */
353 static int
354 load_24T(Biobuf* b, long width, long height, Rgb* buf)
356 long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3;
358 if(height > 0) { /* bottom-up */
359 i = (height - 1) * width;
360 step_up = -2 * width;
361 } else
362 height = -height;
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);
371 Bseek(b, skip, 1);
373 return 0;
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)
380 uchar c[4];
381 long ix, iy, i = 0, step_up = 0;
383 if(height > 0) { /* bottom-up */
384 i = (height - 1) * width;
385 step_up = -2 * width;
386 } else
387 height = -height;
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;
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);
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);
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];
435 return 0;
439 static Rgb*
440 ReadBMP(Biobuf *b, int *width, int *height)
442 int colours, num_coltab = 0;
443 Filehdr bmfh;
444 Infohdr bmih;
445 Rgb clut[256];
446 Rgb* buf;
448 bmfh.type = r16(b);
449 if(bmfh.type != 0x4d42) /* signature must be 'BM' */
450 sysfatal("bad magic number, not a BMP file");
452 bmfh.size = r32(b);
453 bmfh.reserved1 = r16(b);
454 bmfh.reserved2 = r16(b);
455 bmfh.offbits = r32(b);
457 memset(&bmih, 0, sizeof(bmih));
458 bmih.size = r32(b);
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);
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;
490 if(num_coltab) {
491 int i;
492 Bseek(b, bmih.size + sizeof(Infohdr), 0);
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);
502 *width = bmih.width;
503 *height = bmih.height;
504 colours = bmih.bpp;
506 Bseek(b, bmfh.offbits, 0);
508 if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil)
509 sysfatal("no memory");
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;
539 return buf;
542 Rawimage**
543 Breadbmp(Biobuf *bp, int colourspace)
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];
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;
563 if ((bmp = ReadBMP(bp, &width, &height)) == nil)
564 return nil;
566 if ((a = calloc(sizeof(Rawimage), 1)) == nil)
567 goto Error;
569 for (c = 0; c < 3; c++)
570 if ((a->chans[c] = calloc(width, height)) == nil)
571 goto Error;
573 if ((array = calloc(sizeof(Rawimage *), 2)) == nil)
574 goto Error;
575 array[0] = a;
576 array[1] = nil;
578 a->nchans = 3;
579 a->chandesc = CRGB;
580 a->chanlen = width * height;
581 a->r = Rect(0, 0, width, height);
583 s = bmp;
584 e = s + width * height;
585 r = a->chans[0];
586 g = a->chans[1];
587 b = a->chans[2];
589 do {
590 *r++ = s->red;
591 *g++ = s->green;
592 *b++ = s->blue;
593 }while(++s < e);
595 free(bmp);
596 return array;
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;
613 Rawimage**
614 readbmp(int fd, int colorspace)
616 Rawimage * *a;
617 Biobuf b;
619 if (Binit(&b, fd, OREAD) < 0)
620 return nil;
621 a = Breadbmp(&b, colorspace);
622 Bterm(&b);
623 return a;