--- sourceforge.net/trunk/rdesktop/bitmap.c 2000/05/10 07:36:34 4 +++ sourceforge.net/trunk/rdesktop/bitmap.c 2003/03/27 13:08:57 345 @@ -1,263 +1,282 @@ -/* +/* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. Bitmap decompression routines - Copyright (C) Matthew Chapman 1999-2000 - + Copyright (C) Matthew Chapman 1999-2002 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "includes.h" -#include +#include "rdesktop.h" + +#define CVAL(p) (*(p++)) + +static uint32 +cvalx(unsigned char **input, int Bpp) +{ + uint32 rv = 0; + memcpy(&rv, *input, Bpp); + *input += Bpp; + return rv; +} + +static void +setli(unsigned char *input, int offset, uint32 value, int Bpp) +{ + input += offset * Bpp; + memcpy(input, &value, Bpp); +} + +static uint32 +getli(unsigned char *input, int offset, int Bpp) +{ + uint32 rv = 0; + input += offset * Bpp; + memcpy(&rv, input, Bpp); + return rv; +} -#define BITMAP_DEBUG 1 +#define UNROLL8(exp) { exp exp exp exp exp exp exp exp } -#if BITMAP_DEBUG -void hexdump(char *filename, unsigned char *data, int length); -#endif - -#define RCVAL() (*(input++)) -#define RSVAL() ((*((input++) + 1) << 8) | RCVAL()) -#define SCVAL(v) {*(output++) = (v);} - -#define FILL() {while (n-- > 0) { if (output - start < width) { SCVAL(0) } else { SCVAL(*(output-width)); }}} -#define MIX() {while (n-- > 0) { if (output - start < width) { SCVAL(mix) } else { SCVAL(*(output-width) ^ mix); }}} -#define COPY() {while (n-- > 0) { SCVAL(RCVAL()); }} -#define COLOR() {int color = RCVAL(); \ - while (n-- > 0) { SCVAL(color); }} -#define BICOLOR() {int color1 = RCVAL(); int color2 = RCVAL(); \ - while (n-- > 0) { SCVAL(color1); SCVAL(color2); }} -#define SETMIX_MIX() {mix = RCVAL(); MIX();} -#define COPY_PACKED() {n++; n/=2; while (n-- > 0) \ - {unsigned char c = RCVAL(); SCVAL((c & 0xF0) >> 4); \ - SCVAL(c & 0x0F); }} +#define REPEAT(statement) \ +{ \ + while((count & ~0x7) && ((x+8) < width)) \ + UNROLL8( statement; count--; x++; ); \ + \ + while((count > 0) && (x < width)) { statement; count--; x++; } \ +} -BOOL bitmap_decompress(unsigned char *input, int size, - unsigned char *output, int width) +#define MASK_UPDATE() \ +{ \ + mixmask <<= 1; \ + if (mixmask == 0) \ + { \ + mask = fom_mask ? fom_mask : CVAL(input); \ + mixmask = 1; \ + } \ +} + +BOOL +bitmap_decompress(unsigned char *output, int width, int height, unsigned char *input, int size, + int Bpp) { - unsigned char *savedinput = input; - unsigned char *start = output; unsigned char *end = input + size; - unsigned char code; - unsigned char mix = 0xFF; - int n, savedn; + unsigned char *prevline = NULL, *line = NULL; + int opcode, count, offset, isfillormix, x = width; + int lastopcode = -1, insertmix = False, bicolour = False; + uint8 code; + uint32 colour1 = 0, colour2 = 0; + uint8 mixmask, mask = 0; + uint32 mix = 0xffffffff; + int fom_mask = 0; while (input < end) { - code = RCVAL(); - switch (code) + fom_mask = 0; + code = CVAL(input); + opcode = code >> 4; + + /* Handle different opcode forms */ + switch (opcode) + { + case 0xc: + case 0xd: + case 0xe: + opcode -= 6; + count = code & 0xf; + offset = 16; + break; + + case 0xf: + opcode = code & 0xf; + if (opcode < 9) + { + count = CVAL(input); + count |= CVAL(input) << 8; + } + else + { + count = (opcode < 0xb) ? 8 : 1; + } + offset = 0; + break; + + default: + opcode >>= 1; + count = code & 0x1f; + offset = 32; + break; + } + + /* Handle strange cases for counts */ + if (offset != 0) { - case 0x00: // Fill - n = RCVAL() + 32; - FILL(); - break; - case 0xF0: // Fill - n = RSVAL(); - FILL(); - break; - case 0x20: // Mix - n = RCVAL() + 32; - MIX(); - break; - case 0xF1: // Mix - n = RSVAL(); - MIX(); - break; - case 0x40: // FillOrMix - fprintf(stderr, "FillOrMix unsupported\n"); - savedn = n = RCVAL() + 1; - MIX(); - input += (savedn+7)/8; - break; - case 0xF2: - fprintf(stderr, "FillOrMix unsupported\n"); - savedn = n = RSVAL(); - MIX(); - input += (savedn+7)/8; - break; - case 0x60: // Color - n = RCVAL() + 32; - COLOR(); - break; - case 0xF3: - n = RSVAL(); - fprintf(stderr, "Color %d\n", n); - COLOR(); - break; - case 0x80: // Copy - n = RCVAL() + 32; - COPY(); - break; - case 0xF4: - n = RSVAL(); - COPY(); - break; - case 0xA0: // Copy Packed - fprintf(stderr, "CopyPacked 1\n"); - n = RCVAL() + 32; - COPY_PACKED(); - break; - case 0xF5: - fprintf(stderr, "CopyPacked 2\n"); - n = RSVAL(); - COPY_PACKED(); - break; - case 0xC0: // SetMix_Mix - fprintf(stderr, "SetMix_Mix 1\n"); - n = RCVAL() + 16; - SETMIX_MIX(); - break; - case 0xF6: - fprintf(stderr, "SetMix_Mix 2\n"); - n = RSVAL(); - SETMIX_MIX(); - break; - case 0xD0: // SetMix_FillOrMix - fprintf(stderr, "SetMix_FillOrMix unsupported\n"); - savedn = n = RCVAL() + 1; - SETMIX_MIX(); - input += (savedn+7)/8; - break; - case 0xF7: - fprintf(stderr, "SetMix_FillOrMix unsupported\n"); - savedn = n = RSVAL(); - SETMIX_MIX(); - input += (savedn+7)/8; - break; - case 0xE0: // Bicolor - fprintf(stderr, "Bicolor 1\n"); - n = RCVAL() + 16; - BICOLOR(); - break; - case 0xF8: - fprintf(stderr, "Bicolor 2\n"); - n = RSVAL(); - BICOLOR(); - break; - case 0xF9: // FillOrMix_1 - fprintf(stderr, "FillOrMix_1 unsupported\n"); - return False; - case 0xFA: // FillOrMix_2 - fprintf(stderr, "FillOrMix_2 unsupported\n"); - return False; - case 0xFD: // White - SCVAL(0xFF); - break; - case 0xFE: // Black - SCVAL(0); - break; - default: - n = code & 31; + isfillormix = ((opcode == 2) || (opcode == 7)); - if (n == 0) + if (count == 0) { - fprintf(stderr, "Undefined escape 0x%X\n", code); - return False; + if (isfillormix) + count = CVAL(input) + 1; + else + count = CVAL(input) + offset; } - - switch ((code >> 5) & 7) + else if (isfillormix) { - case 0: // Fill - FILL(); - break; - case 1: // Mix - MIX(); + count <<= 3; + } + } + + /* Read preliminary data */ + switch (opcode) + { + case 0: /* Fill */ + if ((lastopcode == opcode) && !((x == width) && (prevline == NULL))) + insertmix = True; break; - case 2: // FillOrMix - fprintf(stderr, "FillOrMix unsupported\n"); - n *= 8; - savedn = n; - MIX(); - input += (savedn+7)/8; + case 8: /* Bicolour */ + colour1 = cvalx(&input, Bpp); + case 3: /* Colour */ + colour2 = cvalx(&input, Bpp); break; - case 3: // Color - COLOR(); + case 6: /* SetMix/Mix */ + case 7: /* SetMix/FillOrMix */ + mix = cvalx(&input, Bpp); + opcode -= 5; break; - case 4: // Copy - COPY(); + case 9: /* FillOrMix_1 */ + mask = 0x03; + opcode = 0x02; + fom_mask = 3; break; - case 5: // Copy Packed - fprintf(stderr, "CopyPacked 3\n"); - COPY_PACKED(); + case 0x0a: /* FillOrMix_2 */ + mask = 0x05; + opcode = 0x02; + fom_mask = 5; break; - case 6: - n = code & 15; - switch ((code >> 4) & 15) - { - case 0xC: - fprintf(stderr, "SetMix_Mix 3\n"); - SETMIX_MIX(); - break; - case 0xD: - fprintf(stderr, "SetMix_FillOrMix unsupported\n"); - n *= 8; - savedn = n; - SETMIX_MIX(); - input += (savedn+7)/8; - break; - case 0xE: - fprintf(stderr, "Bicolor 3\n"); - BICOLOR(); - break; - default: - fprintf(stderr, "Undefined escape 0x%X\n", code); + } + + lastopcode = opcode; + mixmask = 0; + + /* Output body */ + while (count > 0) + { + if (x >= width) + { + if (height <= 0) return False; - } + + x = 0; + height--; + + prevline = line; + line = output + height * width * Bpp; } - } - } - printf("Uncompressed size: %d\n", output - start); -#if BITMAP_DEBUG - { - static int bmpno = 1; - char filename[64]; + switch (opcode) + { + case 0: /* Fill */ + 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; - snprintf(filename, sizeof(filename)-1, "in%d.raw", bmpno); - hexdump(filename, savedinput, size); + case 1: /* Mix */ + if (prevline == NULL) + { + REPEAT(setli(line, x, mix, Bpp)); + } + else + { + REPEAT(setli + (line, x, getli(prevline, x, Bpp) ^ mix, + Bpp)); + } + break; - snprintf(filename, sizeof(filename)-1, "out%d.raw", bmpno); - hexdump(filename, start, output-start); + case 2: /* Fill or Mix */ + if (prevline == NULL) + { + REPEAT(MASK_UPDATE(); + if (mask & mixmask) setli(line, x, mix, Bpp); + else + setli(line, x, 0, Bpp);); + } + else + { + REPEAT(MASK_UPDATE(); + if (mask & mixmask) + setli(line, x, getli(prevline, x, Bpp) ^ mix, + Bpp); + else + setli(line, x, getli(prevline, x, Bpp), + Bpp);); + } + break; - bmpno++; - } -#endif + case 3: /* Colour */ + REPEAT(setli(line, x, colour2, Bpp)); + break; - return True; -} + case 4: /* Copy */ + REPEAT(setli(line, x, cvalx(&input, Bpp), Bpp)); + break; + case 8: /* Bicolour */ + REPEAT(if (bicolour) + { + setli(line, x, colour2, Bpp); bicolour = False;} + else + { + setli(line, x, colour1, Bpp); bicolour = True; + count++;} + ); + break; -#if BITMAP_DEBUG -void hexdump(char *filename, unsigned char *data, int length) -{ - /* - int i; + case 0xd: /* White */ + REPEAT(setli(line, x, 0xffffffff, Bpp)); + break; - for (i = 0; i < length; i++) - { - printf("%02X ", data[i]); + case 0xe: /* Black */ + REPEAT(setli(line, x, 0, Bpp)); + break; - if (i % 16 == 15) - printf("\n"); + default: + unimpl("bitmap opcode 0x%x\n", opcode); + return False; + } + } } - */ - int fd; - - fd = open(filename, O_WRONLY|O_CREAT, 0600); - write(fd, data, length); - close(fd); + return True; } -#endif