1 |
/* |
/* -*- c-basic-offset: 8 -*- |
2 |
rdesktop: A Remote Desktop Protocol client. |
rdesktop: A Remote Desktop Protocol client. |
3 |
Bitmap decompression routines |
Bitmap decompression routines |
4 |
Copyright (C) Matthew Chapman 1999-2002 |
Copyright (C) Matthew Chapman 1999-2007 |
5 |
|
|
6 |
This program is free software; you can redistribute it and/or modify |
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 |
it under the terms of the GNU General Public License as published by |
18 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
*/ |
*/ |
20 |
|
|
21 |
/* three seperate function for speed when decompressing the bitmaps */ |
/* three seperate function for speed when decompressing the bitmaps |
22 |
/* when modifing one function make the change in the others */ |
when modifing one function make the change in the others |
23 |
/* comment out #define BITMAP_SPEED_OVER_SIZE below for one slower function */ |
jay.sorg@gmail.com */ |
|
/* j@american-data.com */ |
|
|
|
|
|
#define BITMAP_SPEED_OVER_SIZE |
|
24 |
|
|
25 |
/* indent is confused by this file */ |
/* indent is confused by this file */ |
26 |
/* *INDENT-OFF* */ |
/* *INDENT-OFF* */ |
28 |
#include "rdesktop.h" |
#include "rdesktop.h" |
29 |
|
|
30 |
#define CVAL(p) (*(p++)) |
#define CVAL(p) (*(p++)) |
31 |
#define CVAL2(p) (*(((uint16*)p)++)) /* for 16 bit */ |
#ifdef NEED_ALIGN |
32 |
|
#ifdef L_ENDIAN |
33 |
|
#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; } |
34 |
|
#else |
35 |
|
#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); } |
36 |
|
#endif /* L_ENDIAN */ |
37 |
|
#else |
38 |
|
#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; } |
39 |
|
#endif /* NEED_ALIGN */ |
40 |
|
|
41 |
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp } |
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp } |
42 |
|
|
63 |
} \ |
} \ |
64 |
} |
} |
65 |
|
|
|
#ifdef BITMAP_SPEED_OVER_SIZE |
|
|
|
|
66 |
/* 1 byte bitmap decompress */ |
/* 1 byte bitmap decompress */ |
67 |
static BOOL |
static RD_BOOL |
68 |
bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) |
bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) |
69 |
{ |
{ |
70 |
uint8 *end = input + size; |
uint8 *end = input + size; |
262 |
} |
} |
263 |
|
|
264 |
/* 2 byte bitmap decompress */ |
/* 2 byte bitmap decompress */ |
265 |
static BOOL |
static RD_BOOL |
266 |
bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size) |
bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size) |
267 |
{ |
{ |
268 |
uint8 *end = input + size; |
uint8 *end = input + size; |
333 |
insertmix = True; |
insertmix = True; |
334 |
break; |
break; |
335 |
case 8: /* Bicolour */ |
case 8: /* Bicolour */ |
336 |
colour1 = CVAL2(input); |
CVAL2(input, colour1); |
337 |
case 3: /* Colour */ |
case 3: /* Colour */ |
338 |
colour2 = CVAL2(input); |
CVAL2(input, colour2); |
339 |
break; |
break; |
340 |
case 6: /* SetMix/Mix */ |
case 6: /* SetMix/Mix */ |
341 |
case 7: /* SetMix/FillOrMix */ |
case 7: /* SetMix/FillOrMix */ |
342 |
mix = CVAL2(input); |
CVAL2(input, mix); |
343 |
opcode -= 5; |
opcode -= 5; |
344 |
break; |
break; |
345 |
case 9: /* FillOrMix_1 */ |
case 9: /* FillOrMix_1 */ |
427 |
REPEAT(line[x] = colour2) |
REPEAT(line[x] = colour2) |
428 |
break; |
break; |
429 |
case 4: /* Copy */ |
case 4: /* Copy */ |
430 |
REPEAT(line[x] = CVAL2(input)) |
REPEAT(CVAL2(input, line[x])) |
431 |
break; |
break; |
432 |
case 8: /* Bicolour */ |
case 8: /* Bicolour */ |
433 |
REPEAT |
REPEAT |
461 |
} |
} |
462 |
|
|
463 |
/* 3 byte bitmap decompress */ |
/* 3 byte bitmap decompress */ |
464 |
static BOOL |
static RD_BOOL |
465 |
bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size) |
bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size) |
466 |
{ |
{ |
467 |
uint8 *end = input + size; |
uint8 *end = input + size; |
746 |
return True; |
return True; |
747 |
} |
} |
748 |
|
|
749 |
#else |
/* decompress a colour plane */ |
750 |
|
static int |
751 |
static uint32 |
process_plane(uint8 * in, int width, int height, uint8 * out, int size) |
|
cvalx(uint8 **input, int Bpp) |
|
|
{ |
|
|
uint32 rv = 0; |
|
|
memcpy(&rv, *input, Bpp); |
|
|
*input += Bpp; |
|
|
return rv; |
|
|
} |
|
|
|
|
|
static void |
|
|
setli(uint8 *input, int offset, uint32 value, int Bpp) |
|
|
{ |
|
|
input += offset * Bpp; |
|
|
memcpy(input, &value, Bpp); |
|
|
} |
|
|
|
|
|
static uint32 |
|
|
getli(uint8 *input, int offset, int Bpp) |
|
|
{ |
|
|
uint32 rv = 0; |
|
|
input += offset * Bpp; |
|
|
memcpy(&rv, input, Bpp); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
static BOOL |
|
|
bitmap_decompressx(uint8 *output, int width, int height, uint8 *input, int size, int Bpp) |
|
752 |
{ |
{ |
753 |
uint8 *end = input + size; |
int indexw; |
754 |
uint8 *prevline = NULL, *line = NULL; |
int indexh; |
755 |
int opcode, count, offset, isfillormix, x = width; |
int code; |
756 |
int lastopcode = -1, insertmix = False, bicolour = False; |
int collen; |
757 |
uint8 code; |
int replen; |
758 |
uint32 colour1 = 0, colour2 = 0; |
int color; |
759 |
uint8 mixmask, mask = 0; |
int x; |
760 |
uint32 mix = 0xffffffff; |
int revcode; |
761 |
int fom_mask = 0; |
uint8 * last_line; |
762 |
|
uint8 * this_line; |
763 |
while (input < end) |
uint8 * org_in; |
764 |
|
uint8 * org_out; |
765 |
|
|
766 |
|
org_in = in; |
767 |
|
org_out = out; |
768 |
|
last_line = 0; |
769 |
|
indexh = 0; |
770 |
|
while (indexh < height) |
771 |
{ |
{ |
772 |
fom_mask = 0; |
out = (org_out + width * height * 4) - ((indexh + 1) * width * 4); |
773 |
code = CVAL(input); |
color = 0; |
774 |
opcode = code >> 4; |
this_line = out; |
775 |
|
indexw = 0; |
776 |
/* Handle different opcode forms */ |
if (last_line == 0) |
777 |
switch (opcode) |
{ |
778 |
{ |
while (indexw < width) |
779 |
case 0xc: |
{ |
780 |
case 0xd: |
code = CVAL(in); |
781 |
case 0xe: |
replen = code & 0xf; |
782 |
opcode -= 6; |
collen = (code >> 4) & 0xf; |
783 |
count = code & 0xf; |
revcode = (replen << 4) | collen; |
784 |
offset = 16; |
if ((revcode <= 47) && (revcode >= 16)) |
|
break; |
|
|
|
|
|
case 0xf: |
|
|
opcode = code & 0xf; |
|
|
if (opcode < 9) |
|
785 |
{ |
{ |
786 |
count = CVAL(input); |
replen = revcode; |
787 |
count |= CVAL(input) << 8; |
collen = 0; |
788 |
} |
} |
789 |
else |
while (collen > 0) |
790 |
{ |
{ |
791 |
count = (opcode < 0xb) ? 8 : 1; |
color = CVAL(in); |
792 |
|
*out = color; |
793 |
|
out += 4; |
794 |
|
indexw++; |
795 |
|
collen--; |
796 |
|
} |
797 |
|
while (replen > 0) |
798 |
|
{ |
799 |
|
*out = color; |
800 |
|
out += 4; |
801 |
|
indexw++; |
802 |
|
replen--; |
803 |
} |
} |
|
offset = 0; |
|
|
break; |
|
|
|
|
|
default: |
|
|
opcode >>= 1; |
|
|
count = code & 0x1f; |
|
|
offset = 32; |
|
|
break; |
|
|
} |
|
|
|
|
|
/* Handle strange cases for counts */ |
|
|
if (offset != 0) |
|
|
{ |
|
|
isfillormix = ((opcode == 2) || (opcode == 7)); |
|
|
|
|
|
if (count == 0) |
|
|
{ |
|
|
if (isfillormix) |
|
|
count = CVAL(input) + 1; |
|
|
else |
|
|
count = CVAL(input) + offset; |
|
|
} |
|
|
else if (isfillormix) |
|
|
{ |
|
|
count <<= 3; |
|
804 |
} |
} |
805 |
} |
} |
806 |
|
else |
|
/* Read preliminary data */ |
|
|
switch (opcode) |
|
|
{ |
|
|
case 0: /* Fill */ |
|
|
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) |
|
|
insertmix = True; |
|
|
break; |
|
|
case 8: /* Bicolour */ |
|
|
colour1 = cvalx(&input, Bpp); |
|
|
case 3: /* Colour */ |
|
|
colour2 = cvalx(&input, Bpp); |
|
|
break; |
|
|
case 6: /* SetMix/Mix */ |
|
|
case 7: /* SetMix/FillOrMix */ |
|
|
mix = cvalx(&input, Bpp); |
|
|
opcode -= 5; |
|
|
break; |
|
|
case 9: /* FillOrMix_1 */ |
|
|
mask = 0x03; |
|
|
opcode = 0x02; |
|
|
fom_mask = 3; |
|
|
break; |
|
|
case 0x0a: /* FillOrMix_2 */ |
|
|
mask = 0x05; |
|
|
opcode = 0x02; |
|
|
fom_mask = 5; |
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
lastopcode = opcode; |
|
|
mixmask = 0; |
|
|
|
|
|
/* Output body */ |
|
|
while (count > 0) |
|
807 |
{ |
{ |
808 |
if (x >= width) |
while (indexw < width) |
809 |
{ |
{ |
810 |
if (height <= 0) |
code = CVAL(in); |
811 |
return False; |
replen = code & 0xf; |
812 |
|
collen = (code >> 4) & 0xf; |
813 |
x = 0; |
revcode = (replen << 4) | collen; |
814 |
height--; |
if ((revcode <= 47) && (revcode >= 16)) |
815 |
|
{ |
816 |
prevline = line; |
replen = revcode; |
817 |
line = output + height * width * Bpp; |
collen = 0; |
818 |
} |
} |
819 |
|
while (collen > 0) |
820 |
switch (opcode) |
{ |
821 |
{ |
x = CVAL(in); |
822 |
case 0: /* Fill */ |
if (x & 1) |
|
if (insertmix) |
|
|
{ |
|
|
if (prevline == NULL) |
|
|
setli(line, x, mix, Bpp); |
|
|
else |
|
|
setli(line, x, |
|
|
getli(prevline, x, Bpp) ^ mix, Bpp); |
|
|
|
|
|
insertmix = False; |
|
|
count--; |
|
|
x++; |
|
|
} |
|
|
|
|
|
if (prevline == NULL) |
|
|
{ |
|
|
REPEAT(setli(line, x, 0, Bpp))} |
|
|
else |
|
|
{ |
|
|
REPEAT(setli |
|
|
(line, x, getli(prevline, x, Bpp), Bpp)); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 1: /* Mix */ |
|
|
if (prevline == NULL) |
|
|
{ |
|
|
REPEAT(setli(line, x, mix, Bpp)); |
|
|
} |
|
|
else |
|
|
{ |
|
|
REPEAT(setli |
|
|
(line, x, getli(prevline, x, Bpp) ^ mix, |
|
|
Bpp)); |
|
|
} |
|
|
break; |
|
|
|
|
|
case 2: /* Fill or Mix */ |
|
|
if (prevline == NULL) |
|
823 |
{ |
{ |
824 |
REPEAT(MASK_UPDATE(); |
x = x >> 1; |
825 |
if (mask & mixmask) setli(line, x, mix, Bpp); |
x = x + 1; |
826 |
else |
color = -x; |
|
setli(line, x, 0, Bpp);); |
|
827 |
} |
} |
828 |
else |
else |
829 |
{ |
{ |
830 |
REPEAT(MASK_UPDATE(); |
x = x >> 1; |
831 |
if (mask & mixmask) |
color = x; |
|
setli(line, x, getli(prevline, x, Bpp) ^ mix, |
|
|
Bpp); |
|
|
else |
|
|
setli(line, x, getli(prevline, x, Bpp), |
|
|
Bpp);); |
|
832 |
} |
} |
833 |
break; |
x = last_line[indexw * 4] + color; |
834 |
|
*out = x; |
835 |
case 3: /* Colour */ |
out += 4; |
836 |
REPEAT(setli(line, x, colour2, Bpp)); |
indexw++; |
837 |
break; |
collen--; |
838 |
|
} |
839 |
case 4: /* Copy */ |
while (replen > 0) |
840 |
REPEAT(setli(line, x, cvalx(&input, Bpp), Bpp)); |
{ |
841 |
break; |
x = last_line[indexw * 4] + color; |
842 |
|
*out = x; |
843 |
case 8: /* Bicolour */ |
out += 4; |
844 |
REPEAT(if (bicolour) |
indexw++; |
845 |
{ |
replen--; |
846 |
setli(line, x, colour2, Bpp); bicolour = False;} |
} |
|
else |
|
|
{ |
|
|
setli(line, x, colour1, Bpp); bicolour = True; |
|
|
count++;} |
|
|
); |
|
|
break; |
|
|
|
|
|
case 0xd: /* White */ |
|
|
REPEAT(setli(line, x, 0xffffffff, Bpp)); |
|
|
break; |
|
|
|
|
|
case 0xe: /* Black */ |
|
|
REPEAT(setli(line, x, 0, Bpp)); |
|
|
break; |
|
|
|
|
|
default: |
|
|
unimpl("bitmap opcode 0x%x\n", opcode); |
|
|
return False; |
|
847 |
} |
} |
848 |
} |
} |
849 |
|
indexh++; |
850 |
|
last_line = this_line; |
851 |
} |
} |
852 |
|
return (int) (in - org_in); |
|
return True; |
|
853 |
} |
} |
854 |
|
|
855 |
#endif |
/* 4 byte bitmap decompress */ |
856 |
|
static RD_BOOL |
857 |
|
bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size) |
858 |
|
{ |
859 |
|
int code; |
860 |
|
int bytes_pro; |
861 |
|
int total_pro; |
862 |
|
|
863 |
|
code = CVAL(input); |
864 |
|
if (code != 0x10) |
865 |
|
{ |
866 |
|
return False; |
867 |
|
} |
868 |
|
total_pro = 1; |
869 |
|
bytes_pro = process_plane(input, width, height, output + 3, size - total_pro); |
870 |
|
total_pro += bytes_pro; |
871 |
|
input += bytes_pro; |
872 |
|
bytes_pro = process_plane(input, width, height, output + 2, size - total_pro); |
873 |
|
total_pro += bytes_pro; |
874 |
|
input += bytes_pro; |
875 |
|
bytes_pro = process_plane(input, width, height, output + 1, size - total_pro); |
876 |
|
total_pro += bytes_pro; |
877 |
|
input += bytes_pro; |
878 |
|
bytes_pro = process_plane(input, width, height, output + 0, size - total_pro); |
879 |
|
total_pro += bytes_pro; |
880 |
|
return size == total_pro; |
881 |
|
} |
882 |
|
|
883 |
/* main decompress function */ |
/* main decompress function */ |
884 |
BOOL |
RD_BOOL |
885 |
bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp) |
bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp) |
886 |
{ |
{ |
887 |
#ifdef BITMAP_SPEED_OVER_SIZE |
RD_BOOL rv = False; |
888 |
BOOL rv = False; |
|
889 |
switch (Bpp) |
switch (Bpp) |
890 |
{ |
{ |
891 |
case 1: |
case 1: |
897 |
case 3: |
case 3: |
898 |
rv = bitmap_decompress3(output, width, height, input, size); |
rv = bitmap_decompress3(output, width, height, input, size); |
899 |
break; |
break; |
900 |
|
case 4: |
901 |
|
rv = bitmap_decompress4(output, width, height, input, size); |
902 |
|
break; |
903 |
|
default: |
904 |
|
unimpl("Bpp %d\n", Bpp); |
905 |
|
break; |
906 |
} |
} |
|
#else |
|
|
BOOL rv; |
|
|
rv = bitmap_decompressx(output, width, height, input, size, Bpp); |
|
|
#endif |
|
907 |
return rv; |
return rv; |
908 |
} |
} |
909 |
|
|