1 /* -*- c-basic-offset: 8 -*- 2 rdesktop: A Remote Desktop Protocol client. 3 Bitmap decompression routines 4 Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /* three separate function for speed when decompressing the bitmaps 21 when modifying one function make the change in the others 22 jay.sorg@gmail.com */ 23 24 /* indent is confused by this file */ 25 /* *INDENT-OFF* */ 26 27 #include "precomp.h" 28 29 #define CVAL(p) (*(p++)) 30 #ifdef NEED_ALIGN 31 #ifdef L_ENDIAN 32 #define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; } 33 #else 34 #define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); } 35 #endif /* L_ENDIAN */ 36 #else 37 #define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; } 38 #endif /* NEED_ALIGN */ 39 40 #define UNROLL8(exp) { exp exp exp exp exp exp exp exp } 41 42 #define REPEAT(statement) \ 43 { \ 44 while((count & ~0x7) && ((x+8) < width)) \ 45 UNROLL8( statement; count--; x++; ); \ 46 \ 47 while((count > 0) && (x < width)) \ 48 { \ 49 statement; \ 50 count--; \ 51 x++; \ 52 } \ 53 } 54 55 #define MASK_UPDATE() \ 56 { \ 57 mixmask <<= 1; \ 58 if (mixmask == 0) \ 59 { \ 60 mask = fom_mask ? fom_mask : CVAL(input); \ 61 mixmask = 1; \ 62 } \ 63 } 64 65 /* 1 byte bitmap decompress */ 66 static RD_BOOL 67 bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) 68 { 69 uint8 *end = input + size; 70 uint8 *prevline = NULL, *line = NULL; 71 int opcode, count, offset, isfillormix, x = width; 72 int lastopcode = -1, insertmix = False, bicolour = False; 73 uint8 code; 74 uint8 colour1 = 0, colour2 = 0; 75 uint8 mixmask, mask = 0; 76 uint8 mix = 0xff; 77 int fom_mask = 0; 78 79 while (input < end) 80 { 81 fom_mask = 0; 82 code = CVAL(input); 83 opcode = code >> 4; 84 /* Handle different opcode forms */ 85 switch (opcode) 86 { 87 case 0xc: 88 case 0xd: 89 case 0xe: 90 opcode -= 6; 91 count = code & 0xf; 92 offset = 16; 93 break; 94 case 0xf: 95 opcode = code & 0xf; 96 if (opcode < 9) 97 { 98 count = CVAL(input); 99 count |= CVAL(input) << 8; 100 } 101 else 102 { 103 count = (opcode < 0xb) ? 8 : 1; 104 } 105 offset = 0; 106 break; 107 default: 108 opcode >>= 1; 109 count = code & 0x1f; 110 offset = 32; 111 break; 112 } 113 /* Handle strange cases for counts */ 114 if (offset != 0) 115 { 116 isfillormix = ((opcode == 2) || (opcode == 7)); 117 if (count == 0) 118 { 119 if (isfillormix) 120 count = CVAL(input) + 1; 121 else 122 count = CVAL(input) + offset; 123 } 124 else if (isfillormix) 125 { 126 count <<= 3; 127 } 128 } 129 /* Read preliminary data */ 130 switch (opcode) 131 { 132 case 0: /* Fill */ 133 if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) 134 insertmix = True; 135 break; 136 case 8: /* Bicolour */ 137 colour1 = CVAL(input); 138 case 3: /* Colour */ 139 colour2 = CVAL(input); 140 break; 141 case 6: /* SetMix/Mix */ 142 case 7: /* SetMix/FillOrMix */ 143 mix = CVAL(input); 144 opcode -= 5; 145 break; 146 case 9: /* FillOrMix_1 */ 147 mask = 0x03; 148 opcode = 0x02; 149 fom_mask = 3; 150 break; 151 case 0x0a: /* FillOrMix_2 */ 152 mask = 0x05; 153 opcode = 0x02; 154 fom_mask = 5; 155 break; 156 } 157 lastopcode = opcode; 158 mixmask = 0; 159 /* Output body */ 160 while (count > 0) 161 { 162 if (x >= width) 163 { 164 if (height <= 0) 165 return False; 166 x = 0; 167 height--; 168 prevline = line; 169 line = output + height * width; 170 } 171 switch (opcode) 172 { 173 case 0: /* Fill */ 174 if (insertmix) 175 { 176 if (prevline == NULL) 177 line[x] = mix; 178 else 179 line[x] = prevline[x] ^ mix; 180 insertmix = False; 181 count--; 182 x++; 183 } 184 if (prevline == NULL) 185 { 186 REPEAT(line[x] = 0) 187 } 188 else 189 { 190 REPEAT(line[x] = prevline[x]) 191 } 192 break; 193 case 1: /* Mix */ 194 if (prevline == NULL) 195 { 196 REPEAT(line[x] = mix) 197 } 198 else 199 { 200 REPEAT(line[x] = prevline[x] ^ mix) 201 } 202 break; 203 case 2: /* Fill or Mix */ 204 if (prevline == NULL) 205 { 206 REPEAT 207 ( 208 MASK_UPDATE(); 209 if (mask & mixmask) 210 line[x] = mix; 211 else 212 line[x] = 0; 213 ) 214 } 215 else 216 { 217 REPEAT 218 ( 219 MASK_UPDATE(); 220 if (mask & mixmask) 221 line[x] = prevline[x] ^ mix; 222 else 223 line[x] = prevline[x]; 224 ) 225 } 226 break; 227 case 3: /* Colour */ 228 REPEAT(line[x] = colour2) 229 break; 230 case 4: /* Copy */ 231 REPEAT(line[x] = CVAL(input)) 232 break; 233 case 8: /* Bicolour */ 234 REPEAT 235 ( 236 if (bicolour) 237 { 238 line[x] = colour2; 239 bicolour = False; 240 } 241 else 242 { 243 line[x] = colour1; 244 bicolour = True; count++; 245 } 246 ) 247 break; 248 case 0xd: /* White */ 249 REPEAT(line[x] = 0xff) 250 break; 251 case 0xe: /* Black */ 252 REPEAT(line[x] = 0) 253 break; 254 default: 255 unimpl("bitmap opcode 0x%x\n", opcode); 256 return False; 257 } 258 } 259 } 260 return True; 261 } 262 263 /* 2 byte bitmap decompress */ 264 static RD_BOOL 265 bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size) 266 { 267 uint8 *end = input + size; 268 uint16 *prevline = NULL, *line = NULL; 269 int opcode, count, offset, isfillormix, x = width; 270 int lastopcode = -1, insertmix = False, bicolour = False; 271 uint8 code; 272 uint16 colour1 = 0, colour2 = 0; 273 uint8 mixmask, mask = 0; 274 uint16 mix = 0xffff; 275 int fom_mask = 0; 276 277 while (input < end) 278 { 279 fom_mask = 0; 280 code = CVAL(input); 281 opcode = code >> 4; 282 /* Handle different opcode forms */ 283 switch (opcode) 284 { 285 case 0xc: 286 case 0xd: 287 case 0xe: 288 opcode -= 6; 289 count = code & 0xf; 290 offset = 16; 291 break; 292 case 0xf: 293 opcode = code & 0xf; 294 if (opcode < 9) 295 { 296 count = CVAL(input); 297 count |= CVAL(input) << 8; 298 } 299 else 300 { 301 count = (opcode < 0xb) ? 8 : 1; 302 } 303 offset = 0; 304 break; 305 default: 306 opcode >>= 1; 307 count = code & 0x1f; 308 offset = 32; 309 break; 310 } 311 /* Handle strange cases for counts */ 312 if (offset != 0) 313 { 314 isfillormix = ((opcode == 2) || (opcode == 7)); 315 if (count == 0) 316 { 317 if (isfillormix) 318 count = CVAL(input) + 1; 319 else 320 count = CVAL(input) + offset; 321 } 322 else if (isfillormix) 323 { 324 count <<= 3; 325 } 326 } 327 /* Read preliminary data */ 328 switch (opcode) 329 { 330 case 0: /* Fill */ 331 if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) 332 insertmix = True; 333 break; 334 case 8: /* Bicolour */ 335 CVAL2(input, colour1); 336 case 3: /* Colour */ 337 CVAL2(input, colour2); 338 break; 339 case 6: /* SetMix/Mix */ 340 case 7: /* SetMix/FillOrMix */ 341 CVAL2(input, mix); 342 opcode -= 5; 343 break; 344 case 9: /* FillOrMix_1 */ 345 mask = 0x03; 346 opcode = 0x02; 347 fom_mask = 3; 348 break; 349 case 0x0a: /* FillOrMix_2 */ 350 mask = 0x05; 351 opcode = 0x02; 352 fom_mask = 5; 353 break; 354 } 355 lastopcode = opcode; 356 mixmask = 0; 357 /* Output body */ 358 while (count > 0) 359 { 360 if (x >= width) 361 { 362 if (height <= 0) 363 return False; 364 x = 0; 365 height--; 366 prevline = line; 367 line = ((uint16 *) output) + height * width; 368 } 369 switch (opcode) 370 { 371 case 0: /* Fill */ 372 if (insertmix) 373 { 374 if (prevline == NULL) 375 line[x] = mix; 376 else 377 line[x] = prevline[x] ^ mix; 378 insertmix = False; 379 count--; 380 x++; 381 } 382 if (prevline == NULL) 383 { 384 REPEAT(line[x] = 0) 385 } 386 else 387 { 388 REPEAT(line[x] = prevline[x]) 389 } 390 break; 391 case 1: /* Mix */ 392 if (prevline == NULL) 393 { 394 REPEAT(line[x] = mix) 395 } 396 else 397 { 398 REPEAT(line[x] = prevline[x] ^ mix) 399 } 400 break; 401 case 2: /* Fill or Mix */ 402 if (prevline == NULL) 403 { 404 REPEAT 405 ( 406 MASK_UPDATE(); 407 if (mask & mixmask) 408 line[x] = mix; 409 else 410 line[x] = 0; 411 ) 412 } 413 else 414 { 415 REPEAT 416 ( 417 MASK_UPDATE(); 418 if (mask & mixmask) 419 line[x] = prevline[x] ^ mix; 420 else 421 line[x] = prevline[x]; 422 ) 423 } 424 break; 425 case 3: /* Colour */ 426 REPEAT(line[x] = colour2) 427 break; 428 case 4: /* Copy */ 429 REPEAT(CVAL2(input, line[x])) 430 break; 431 case 8: /* Bicolour */ 432 REPEAT 433 ( 434 if (bicolour) 435 { 436 line[x] = colour2; 437 bicolour = False; 438 } 439 else 440 { 441 line[x] = colour1; 442 bicolour = True; 443 count++; 444 } 445 ) 446 break; 447 case 0xd: /* White */ 448 REPEAT(line[x] = 0xffff) 449 break; 450 case 0xe: /* Black */ 451 REPEAT(line[x] = 0) 452 break; 453 default: 454 unimpl("bitmap opcode 0x%x\n", opcode); 455 return False; 456 } 457 } 458 } 459 return True; 460 } 461 462 /* 3 byte bitmap decompress */ 463 static RD_BOOL 464 bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size) 465 { 466 uint8 *end = input + size; 467 uint8 *prevline = NULL, *line = NULL; 468 int opcode, count, offset, isfillormix, x = width; 469 int lastopcode = -1, insertmix = False, bicolour = False; 470 uint8 code; 471 uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0}; 472 uint8 mixmask, mask = 0; 473 uint8 mix[3] = {0xff, 0xff, 0xff}; 474 int fom_mask = 0; 475 476 while (input < end) 477 { 478 fom_mask = 0; 479 code = CVAL(input); 480 opcode = code >> 4; 481 /* Handle different opcode forms */ 482 switch (opcode) 483 { 484 case 0xc: 485 case 0xd: 486 case 0xe: 487 opcode -= 6; 488 count = code & 0xf; 489 offset = 16; 490 break; 491 case 0xf: 492 opcode = code & 0xf; 493 if (opcode < 9) 494 { 495 count = CVAL(input); 496 count |= CVAL(input) << 8; 497 } 498 else 499 { 500 count = (opcode < 501 0xb) ? 8 : 1; 502 } 503 offset = 0; 504 break; 505 default: 506 opcode >>= 1; 507 count = code & 0x1f; 508 offset = 32; 509 break; 510 } 511 /* Handle strange cases for counts */ 512 if (offset != 0) 513 { 514 isfillormix = ((opcode == 2) || (opcode == 7)); 515 if (count == 0) 516 { 517 if (isfillormix) 518 count = CVAL(input) + 1; 519 else 520 count = CVAL(input) + offset; 521 } 522 else if (isfillormix) 523 { 524 count <<= 3; 525 } 526 } 527 /* Read preliminary data */ 528 switch (opcode) 529 { 530 case 0: /* Fill */ 531 if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) 532 insertmix = True; 533 break; 534 case 8: /* Bicolour */ 535 colour1[0] = CVAL(input); 536 colour1[1] = CVAL(input); 537 colour1[2] = CVAL(input); 538 case 3: /* Colour */ 539 colour2[0] = CVAL(input); 540 colour2[1] = CVAL(input); 541 colour2[2] = CVAL(input); 542 break; 543 case 6: /* SetMix/Mix */ 544 case 7: /* SetMix/FillOrMix */ 545 mix[0] = CVAL(input); 546 mix[1] = CVAL(input); 547 mix[2] = CVAL(input); 548 opcode -= 5; 549 break; 550 case 9: /* FillOrMix_1 */ 551 mask = 0x03; 552 opcode = 0x02; 553 fom_mask = 3; 554 break; 555 case 0x0a: /* FillOrMix_2 */ 556 mask = 0x05; 557 opcode = 0x02; 558 fom_mask = 5; 559 break; 560 } 561 lastopcode = opcode; 562 mixmask = 0; 563 /* Output body */ 564 while (count > 0) 565 { 566 if (x >= width) 567 { 568 if (height <= 0) 569 return False; 570 x = 0; 571 height--; 572 prevline = line; 573 line = output + height * (width * 3); 574 } 575 switch (opcode) 576 { 577 case 0: /* Fill */ 578 if (insertmix) 579 { 580 if (prevline == NULL) 581 { 582 line[x * 3] = mix[0]; 583 line[x * 3 + 1] = mix[1]; 584 line[x * 3 + 2] = mix[2]; 585 } 586 else 587 { 588 line[x * 3] = 589 prevline[x * 3] ^ mix[0]; 590 line[x * 3 + 1] = 591 prevline[x * 3 + 1] ^ mix[1]; 592 line[x * 3 + 2] = 593 prevline[x * 3 + 2] ^ mix[2]; 594 } 595 insertmix = False; 596 count--; 597 x++; 598 } 599 if (prevline == NULL) 600 { 601 REPEAT 602 ( 603 line[x * 3] = 0; 604 line[x * 3 + 1] = 0; 605 line[x * 3 + 2] = 0; 606 ) 607 } 608 else 609 { 610 REPEAT 611 ( 612 line[x * 3] = prevline[x * 3]; 613 line[x * 3 + 1] = prevline[x * 3 + 1]; 614 line[x * 3 + 2] = prevline[x * 3 + 2]; 615 ) 616 } 617 break; 618 case 1: /* Mix */ 619 if (prevline == NULL) 620 { 621 REPEAT 622 ( 623 line[x * 3] = mix[0]; 624 line[x * 3 + 1] = mix[1]; 625 line[x * 3 + 2] = mix[2]; 626 ) 627 } 628 else 629 { 630 REPEAT 631 ( 632 line[x * 3] = 633 prevline[x * 3] ^ mix[0]; 634 line[x * 3 + 1] = 635 prevline[x * 3 + 1] ^ mix[1]; 636 line[x * 3 + 2] = 637 prevline[x * 3 + 2] ^ mix[2]; 638 ) 639 } 640 break; 641 case 2: /* Fill or Mix */ 642 if (prevline == NULL) 643 { 644 REPEAT 645 ( 646 MASK_UPDATE(); 647 if (mask & mixmask) 648 { 649 line[x * 3] = mix[0]; 650 line[x * 3 + 1] = mix[1]; 651 line[x * 3 + 2] = mix[2]; 652 } 653 else 654 { 655 line[x * 3] = 0; 656 line[x * 3 + 1] = 0; 657 line[x * 3 + 2] = 0; 658 } 659 ) 660 } 661 else 662 { 663 REPEAT 664 ( 665 MASK_UPDATE(); 666 if (mask & mixmask) 667 { 668 line[x * 3] = 669 prevline[x * 3] ^ mix [0]; 670 line[x * 3 + 1] = 671 prevline[x * 3 + 1] ^ mix [1]; 672 line[x * 3 + 2] = 673 prevline[x * 3 + 2] ^ mix [2]; 674 } 675 else 676 { 677 line[x * 3] = 678 prevline[x * 3]; 679 line[x * 3 + 1] = 680 prevline[x * 3 + 1]; 681 line[x * 3 + 2] = 682 prevline[x * 3 + 2]; 683 } 684 ) 685 } 686 break; 687 case 3: /* Colour */ 688 REPEAT 689 ( 690 line[x * 3] = colour2 [0]; 691 line[x * 3 + 1] = colour2 [1]; 692 line[x * 3 + 2] = colour2 [2]; 693 ) 694 break; 695 case 4: /* Copy */ 696 REPEAT 697 ( 698 line[x * 3] = CVAL(input); 699 line[x * 3 + 1] = CVAL(input); 700 line[x * 3 + 2] = CVAL(input); 701 ) 702 break; 703 case 8: /* Bicolour */ 704 REPEAT 705 ( 706 if (bicolour) 707 { 708 line[x * 3] = colour2[0]; 709 line[x * 3 + 1] = colour2[1]; 710 line[x * 3 + 2] = colour2[2]; 711 bicolour = False; 712 } 713 else 714 { 715 line[x * 3] = colour1[0]; 716 line[x * 3 + 1] = colour1[1]; 717 line[x * 3 + 2] = colour1[2]; 718 bicolour = True; 719 count++; 720 } 721 ) 722 break; 723 case 0xd: /* White */ 724 REPEAT 725 ( 726 line[x * 3] = 0xff; 727 line[x * 3 + 1] = 0xff; 728 line[x * 3 + 2] = 0xff; 729 ) 730 break; 731 case 0xe: /* Black */ 732 REPEAT 733 ( 734 line[x * 3] = 0; 735 line[x * 3 + 1] = 0; 736 line[x * 3 + 2] = 0; 737 ) 738 break; 739 default: 740 unimpl("bitmap opcode 0x%x\n", opcode); 741 return False; 742 } 743 } 744 } 745 return True; 746 } 747 748 /* decompress a colour plane */ 749 static int 750 process_plane(uint8 * in, int width, int height, uint8 * out, int size) 751 { 752 int indexw; 753 int indexh; 754 int code; 755 int collen; 756 int replen; 757 int color; 758 int x; 759 int revcode; 760 uint8 * last_line; 761 uint8 * this_line; 762 uint8 * org_in; 763 uint8 * org_out; 764 765 org_in = in; 766 org_out = out; 767 last_line = 0; 768 indexh = 0; 769 while (indexh < height) 770 { 771 out = (org_out + width * height * 4) - ((indexh + 1) * width * 4); 772 color = 0; 773 this_line = out; 774 indexw = 0; 775 if (last_line == 0) 776 { 777 while (indexw < width) 778 { 779 code = CVAL(in); 780 replen = code & 0xf; 781 collen = (code >> 4) & 0xf; 782 revcode = (replen << 4) | collen; 783 if ((revcode <= 47) && (revcode >= 16)) 784 { 785 replen = revcode; 786 collen = 0; 787 } 788 while (collen > 0) 789 { 790 color = CVAL(in); 791 *out = color; 792 out += 4; 793 indexw++; 794 collen--; 795 } 796 while (replen > 0) 797 { 798 *out = color; 799 out += 4; 800 indexw++; 801 replen--; 802 } 803 } 804 } 805 else 806 { 807 while (indexw < width) 808 { 809 code = CVAL(in); 810 replen = code & 0xf; 811 collen = (code >> 4) & 0xf; 812 revcode = (replen << 4) | collen; 813 if ((revcode <= 47) && (revcode >= 16)) 814 { 815 replen = revcode; 816 collen = 0; 817 } 818 while (collen > 0) 819 { 820 x = CVAL(in); 821 if (x & 1) 822 { 823 x = x >> 1; 824 x = x + 1; 825 color = -x; 826 } 827 else 828 { 829 x = x >> 1; 830 color = x; 831 } 832 x = last_line[indexw * 4] + color; 833 *out = x; 834 out += 4; 835 indexw++; 836 collen--; 837 } 838 while (replen > 0) 839 { 840 x = last_line[indexw * 4] + color; 841 *out = x; 842 out += 4; 843 indexw++; 844 replen--; 845 } 846 } 847 } 848 indexh++; 849 last_line = this_line; 850 } 851 return (int) (in - org_in); 852 } 853 854 /* 4 byte bitmap decompress */ 855 static RD_BOOL 856 bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size) 857 { 858 int code; 859 int bytes_pro; 860 int total_pro; 861 862 code = CVAL(input); 863 if (code != 0x10) 864 { 865 return False; 866 } 867 total_pro = 1; 868 bytes_pro = process_plane(input, width, height, output + 3, size - total_pro); 869 total_pro += bytes_pro; 870 input += bytes_pro; 871 bytes_pro = process_plane(input, width, height, output + 2, size - total_pro); 872 total_pro += bytes_pro; 873 input += bytes_pro; 874 bytes_pro = process_plane(input, width, height, output + 1, size - total_pro); 875 total_pro += bytes_pro; 876 input += bytes_pro; 877 bytes_pro = process_plane(input, width, height, output + 0, size - total_pro); 878 total_pro += bytes_pro; 879 return size == total_pro; 880 } 881 882 /* main decompress function */ 883 RD_BOOL 884 bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp) 885 { 886 RD_BOOL rv = False; 887 888 switch (Bpp) 889 { 890 case 1: 891 rv = bitmap_decompress1(output, width, height, input, size); 892 break; 893 case 2: 894 rv = bitmap_decompress2(output, width, height, input, size); 895 break; 896 case 3: 897 rv = bitmap_decompress3(output, width, height, input, size); 898 break; 899 case 4: 900 rv = bitmap_decompress4(output, width, height, input, size); 901 break; 902 default: 903 unimpl("Bpp %d\n", Bpp); 904 break; 905 } 906 return rv; 907 } 908 909 /* *INDENT-ON* */ 910