readbmp.c (13692B)
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 = 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(0 < val) { 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 = Bgetc(b); 179 ix += val; 180 val = 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(0 < val) { 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 + sizeof(Infohdr), 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 }