/**
   * Decompress bitmap data from packet and store in array of bytes
   *
   * @param width Width of bitmap
   * @param height Height of bitmap
   * @param size Size of compressed data in bytes
   * @param data Packet containing bitmap data
   * @param Bpp Bytes per-pixel for bitmap
   * @return Byte array of pixels containing decompressed bitmap data
   * @throws RdesktopException
   */
  public static byte[] decompress(
      int width, int height, int size, RdpPacket_Localised data, int Bpp) throws RdesktopException {

    byte[] compressed_pixel = new byte[size];
    data.copyToByteArray(compressed_pixel, 0, data.getPosition(), size);
    data.incrementPosition(size);

    int previous = 0, line = 0;
    int input = 0, output = 0, end = size;
    int opcode = 0, count = 0, offset = 0, x = width;
    int lastopcode = -1, fom_mask = 0;
    int code = 0, color1 = 0, color2 = 0;
    byte mixmask = 0;
    int mask = 0;
    int mix = 0xffffffff;

    boolean insertmix = false, bicolor = false, isfillormix = false;

    byte[] pixel = new byte[width * height];
    while (input < end) {
      fom_mask = 0;
      code = (compressed_pixel[input++] & 0x000000ff);
      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 = (compressed_pixel[input++] & 0xff);
            count |= ((compressed_pixel[input++] & 0xff) << 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) {
        isfillormix = ((opcode == 2) || (opcode == 7));

        if (count == 0) {
          if (isfillormix) count = (compressed_pixel[input++] & 0x000000ff) + 1;
          else count = (compressed_pixel[input++] & 0x000000ff) + offset;
        } else if (isfillormix) {
          count <<= 3;
        }
      }

      switch (opcode) {
        case 0: /* Fill */
          if ((lastopcode == opcode) && !((x == width) && (previous == 0))) insertmix = true;
          break;
        case 8: /* Bicolor */
          // color1 = cvalx(compressed_pixel, input, Bpp); //
          // (compressed_pixel[input++]&0x000000ff);
          color1 = (compressed_pixel[input++] & 0x000000ff);
          // input += Bpp;
        case 3: /* Color */
          // color2 = cvalx(compressed_pixel, input, Bpp);
          color2 = (compressed_pixel[input++] & 0x000000ff);
          // input += Bpp;
          break;
        case 6: /* SetMix/Mix */
        case 7: /* SetMix/FillOrMix */
          mix = compressed_pixel[input++];
          // mix = cvalx(compressed_pixel, input, Bpp);
          // 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) {
        if (x >= width) {
          if (height <= 0)
            throw new RdesktopException("Decompressing bitmap failed! Height = " + height);

          x = 0;
          height--;

          previous = line;
          line = output + height * width;
        }

        switch (opcode) {
          case 0: /* Fill */
            if (insertmix) {
              if (previous == 0) {
                pixel[line + x] = (byte) mix;
              } else {
                // setli(pixel, line, x, getli(pixel, previous, x,
                // Bpp) ^ mix, Bpp);
                pixel[line + x] = (byte) (pixel[previous + x] ^ (byte) mix);
              }

              insertmix = false;
              count--;
              x++;
            }

            if (previous == 0) {
              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  // setli(pixel, line, x, 0, Bpp);
                  pixel[line + x] = 0;
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                // setli(pixel, line, x, 0, Bpp);
                pixel[line + x] = 0;
                count--;
                x++;
              }
            } else {
              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  // setli(pixel, line, x, getli(pixel, previous,
                  // x, Bpp), Bpp);
                  pixel[line + x] = pixel[previous + x];
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                // setli(pixel, line, x, getli(pixel, previous, x,
                // Bpp), Bpp);
                pixel[line + x] = pixel[previous + x];
                count--;
                x++;
              }
            }
            break;

          case 1: /* Mix */
            if (previous == 0) {
              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  // setli(pixel, line, x, mix, Bpp);
                  pixel[line + x] = (byte) mix;
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                // setli(pixel, line, x, mix, Bpp);
                pixel[line + x] = (byte) mix;
                count--;
                x++;
              }
            } else {

              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  setli(pixel, line, x, getli(pixel, previous, x, 1) ^ mix, 1);
                  // setli(pixel, line, x, 0, Bpp);
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                setli(pixel, line, x, getli(pixel, previous, x, 1) ^ mix, 1);
                // setli(pixel, line, x, 0, Bpp);
                count--;
                x++;
              }
            }
            break;
          case 2: /* Fill or Mix */
            if (previous == 0) {
              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  mixmask <<= 1;
                  if (mixmask == 0) {
                    mask = (fom_mask != 0) ? (byte) fom_mask : compressed_pixel[input++];
                    mixmask = 1;
                  }
                  if ((mask & mixmask) != 0)
                    // setli(pixel, line, x, mix, Bpp);
                    pixel[line + x] = (byte) mix;
                  else
                    // setli(pixel, line, x, 0, Bpp);
                    pixel[line + x] = 0;
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                mixmask <<= 1;
                if (mixmask == 0) {
                  mask = (fom_mask != 0) ? (byte) fom_mask : compressed_pixel[input++];
                  mixmask = 1;
                }
                if ((mask & mixmask) != 0)
                  // setli(pixel, line, x, mix, Bpp);
                  pixel[line + x] = (byte) mix;
                else
                  // setli(pixel, line, x, 0, Bpp);
                  pixel[line + x] = 0;
                count--;
                x++;
              }
            } else {
              while (((count & ~0x7) != 0) && ((x + 8) < width)) {
                for (int i = 0; i < 8; i++) {
                  mixmask <<= 1;
                  if (mixmask == 0) {
                    mask = (fom_mask != 0) ? (byte) fom_mask : compressed_pixel[input++];
                    mixmask = 1;
                  }
                  if ((mask & mixmask) != 0)
                    // setli(pixel, line, x, getli(pixel,
                    // previous, x, Bpp)
                    // ^ mix, Bpp);
                    pixel[line + x] = (byte) (pixel[previous + x] ^ (byte) mix);
                  else
                    // setli(pixel, line, x, getli(pixel,
                    // previous, x, Bpp), Bpp);
                    pixel[line + x] = pixel[previous + x];
                  count--;
                  x++;
                }
              }
              while ((count > 0) && (x < width)) {
                mixmask <<= 1;
                if (mixmask == 0) {
                  mask = (fom_mask != 0) ? (byte) fom_mask : compressed_pixel[input++];
                  mixmask = 1;
                }
                if ((mask & mixmask) != 0)
                  // setli(pixel, line, x, getli(pixel, previous,
                  // x,
                  // Bpp)
                  // ^ mix, Bpp);
                  pixel[line + x] = (byte) (pixel[previous + x] ^ (byte) mix);
                else
                  // setli(pixel, line, x, getli(pixel, previous,
                  // x,
                  // Bpp), Bpp);
                  pixel[line + x] = pixel[previous + x];
                count--;
                x++;
              }
            }
            break;

          case 3: /* Color */
            while (((count & ~0x7) != 0) && ((x + 8) < width)) {
              for (int i = 0; i < 8; i++) {
                // setli(pixel, line, x, color2, Bpp);
                pixel[line + x] = (byte) color2;
                count--;
                x++;
              }
            }
            while ((count > 0) && (x < width)) {
              // setli(pixel, line, x, color2, Bpp);
              pixel[line + x] = (byte) color2;
              count--;
              x++;
            }

            break;

          case 4: /* Copy */
            while (((count & ~0x7) != 0) && ((x + 8) < width)) {
              for (int i = 0; i < 8; i++) {
                // setli(pixel, line, x, cvalx(compressed_pixel,
                // input, Bpp), Bpp);
                pixel[line + x] = compressed_pixel[input++];
                // input += Bpp;
                count--;
                x++;
              }
            }
            while ((count > 0) && (x < width)) {
              // setli(pixel, line, x, cvalx(compressed_pixel, input,
              // Bpp), Bpp);
              // input += Bpp;
              pixel[line + x] = compressed_pixel[input++];
              count--;
              x++;
            }
            break;

          case 8: /* Bicolor */
            while (((count & ~0x7) != 0) && ((x + 8) < width)) {
              for (int i = 0; i < 8; i++) {
                if (bicolor) {
                  // setli(pixel, line, x, color2, Bpp);
                  pixel[line + x] = (byte) color2;
                  bicolor = false;
                } else {
                  // setli(pixel, line, x, color1, Bpp);
                  pixel[line + x] = (byte) color1;
                  bicolor = true;
                  count++;
                }
                count--;
                x++;
              }
            }
            while ((count > 0) && (x < width)) {
              if (bicolor) {
                // setli(pixel, line, x, color2, Bpp);
                pixel[line + x] = (byte) color2;
                bicolor = false;
              } else {
                // setli(pixel, line, x, color1, Bpp);
                pixel[line + x] = (byte) color1;
                bicolor = true;
                count++;
              }
              count--;
              x++;
            }

            break;

          case 0xd: /* White */
            while (((count & ~0x7) != 0) && ((x + 8) < width)) {
              for (int i = 0; i < 8; i++) {
                // setli(pixel, line, x, 0xffffffff, Bpp);
                pixel[line + x] = (byte) 0xff;
                count--;
                x++;
              }
            }
            while ((count > 0) && (x < width)) {
              // setli(pixel, line, x, 0xffffffff, Bpp);
              pixel[line + x] = (byte) 0xff;
              count--;
              x++;
            }
            break;

          case 0xe: /* Black */
            while (((count & ~0x7) != 0) && ((x + 8) < width)) {
              for (int i = 0; i < 8; i++) {
                // setli(pixel, line, x, 0, Bpp);
                pixel[line + x] = (byte) 0x00;
                count--;
                x++;
              }
            }
            while ((count > 0) && (x < width)) {
              // setli(pixel, line, x, 0, Bpp);
              pixel[line + x] = (byte) 0x00;
              count--;
              x++;
            }

            break;
          default:
            throw new RdesktopException("Unimplemented decompress opcode " + opcode); // ;
        }
      }
    }

    bmpCount++;

    return pixel;
  }