123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
- * Copyright 2004-2005 Cendio AB.
- *
- * This 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 software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
- package com.tigervnc.rfb;
-
- import com.tigervnc.rdr.InStream;
- import com.tigervnc.rdr.ZlibInStream;
- import java.awt.image.PixelGrabber;
- import java.awt.Image;
- import java.util.ArrayList;
-
- public class TightDecoder extends Decoder {
-
- final static int TIGHT_MAX_WIDTH = 2048;
-
- // Compression control
- final static int rfbTightExplicitFilter = 0x04;
- final static int rfbTightFill = 0x08;
- final static int rfbTightJpeg = 0x09;
- final static int rfbTightMaxSubencoding = 0x09;
-
- // Filters to improve compression efficiency
- final static int rfbTightFilterCopy = 0x00;
- final static int rfbTightFilterPalette = 0x01;
- final static int rfbTightFilterGradient = 0x02;
- final static int rfbTightMinToCompress = 12;
-
- public TightDecoder(CMsgReader reader_) {
- reader = reader_;
- zis = new ZlibInStream[4];
- for (int i = 0; i < 4; i++)
- zis[i] = new ZlibInStream();
- }
-
- public void readRect(Rect r, CMsgHandler handler)
- {
- InStream is = reader.getInStream();
- int[] buf = reader.getImageBuf(r.width() * r.height());
- boolean cutZeros = false;
- PixelFormat myFormat = handler.cp.pf();
- int bpp = handler.cp.pf().bpp;
- if (bpp == 32) {
- if (myFormat.is888()) {
- cutZeros = true;
- }
- }
-
- int comp_ctl = is.readU8();
-
- int bytesPerPixel = handler.cp.pf().bpp / 8;
- boolean bigEndian = handler.cp.pf().bigEndian;
-
- // Flush zlib streams if we are told by the server to do so.
- for (int i = 0; i < 4; i++) {
- if ((comp_ctl & 1) != 0) {
- zis[i].reset();
- }
- comp_ctl >>= 1;
- }
-
- // "Fill" compression type.
- if (comp_ctl == rfbTightFill) {
- int pix;
- if (cutZeros) {
- byte[] elem = new byte[3];
- is.readBytes(elem, 0, 3);
- if (bigEndian) {
- pix =
- (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
- } else {
- pix =
- (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
- }
- } else {
- pix = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
- }
- handler.fillRect(r, pix);
- return;
- }
-
- // "JPEG" compression type.
- if (comp_ctl == rfbTightJpeg) {
- // Read length
- int compressedLen = is.readCompactLength();
- if (compressedLen <= 0)
- vlog.info("Incorrect data received from the server.");
-
- // Allocate netbuf and read in data
- byte[] netbuf = new byte[compressedLen];
- is.readBytes(netbuf, 0, compressedLen);
-
- // Create an Image object from the JPEG data.
- Image jpeg = java.awt.Toolkit.getDefaultToolkit().createImage(netbuf);
- PixelGrabber pg = new PixelGrabber(jpeg, 0, 0, r.width(), r.height(), true);
- try {
- boolean ret = pg.grabPixels();
- if (!ret)
- vlog.info("failed to grab pixels");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Object pixels = pg.getPixels();
- buf = (pixels instanceof byte[]) ?
- convertByteArrayToIntArray((byte[])pixels) : (int[])pixels;
- handler.imageRect(r, buf);
- return;
- }
-
- // Quit on unsupported compression type.
- if (comp_ctl > rfbTightMaxSubencoding) {
- throw new Exception("TightDecoder: bad subencoding value received");
- }
-
- // "Basic" compression type.
- int palSize = 0;
- int[] palette = new int[256];
- boolean useGradient = false;
-
- if ((comp_ctl & rfbTightExplicitFilter) != 0) {
- int filterId = is.readU8();
-
- switch (filterId) {
- case rfbTightFilterPalette:
- palSize = is.readU8() + 1;
- if (cutZeros) {
- byte[] elem = new byte[3];
- for (int i = 0; i < palSize; i++) {
- is.readBytes(elem, 0, 3);
- if (bigEndian) {
- palette[i] =
- (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
- } else {
- palette[i] =
- (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
- }
- }
- } else {
- for (int i = 0; i < palSize; i++) {
- palette[i] = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B();
- }
- }
- break;
- case rfbTightFilterGradient:
- useGradient = true;
- break;
- case rfbTightFilterCopy:
- break;
- default:
- throw new Exception("TightDecoder: unknown filter code recieved");
- }
- }
-
- int bppp = bpp;
- if (palSize != 0) {
- bppp = (palSize <= 2) ? 1 : 8;
- } else if (cutZeros) {
- bppp = 24;
- }
-
- // Determine if the data should be decompressed or just copied.
- int rowSize = (r.width() * bppp + 7) / 8;
- int dataSize = r.height() * rowSize;
- int streamId = -1;
- InStream input;
- if (dataSize < rfbTightMinToCompress) {
- input = is;
- } else {
- int length = is.readCompactLength();
- streamId = comp_ctl & 0x03;
- zis[streamId].setUnderlying(is, length);
- input = (ZlibInStream)zis[streamId];
- }
-
- if (palSize == 0) {
- // Truecolor data.
- if (useGradient) {
- vlog.info("useGradient");
- if (bpp == 32 && cutZeros) {
- vlog.info("FilterGradient24");
- FilterGradient24(r, input, dataSize, buf, handler);
- } else {
- vlog.info("FilterGradient");
- FilterGradient(r, input, dataSize, buf, handler);
- }
- } else {
- if (cutZeros) {
- byte[] elem = new byte[3];
- for (int i = 0; i < r.area(); i++) {
- input.readBytes(elem, 0, 3);
- if (bigEndian) {
- buf[i] =
- (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24;
- } else {
- buf[i] =
- (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24;
- }
- }
- } else {
- for (int ptr=0; ptr < dataSize; ptr++)
- buf[ptr] = input.readU8();
- }
- }
- } else {
- int x, y, b;
- int ptr = 0;
- int bits;
- if (palSize <= 2) {
- // 2-color palette
- for (y = 0; y < r.height(); y++) {
- for (x = 0; x < r.width() / 8; x++) {
- bits = input.readU8();
- for(b = 7; b >= 0; b--) {
- buf[ptr++] = palette[bits >> b & 1];
- }
- }
- if (r.width() % 8 != 0) {
- bits = input.readU8();
- for (b = 7; b >= 8 - r.width() % 8; b--) {
- buf[ptr++] = palette[bits >> b & 1];
- }
- }
- }
- } else {
- // 256-color palette
- for (y = 0; y < r.height(); y++) {
- for (x = 0; x < r.width(); x++) {
- buf[ptr++] = palette[input.readU8()];
- }
- }
- }
- }
-
- handler.imageRect(r, buf);
-
- if (streamId != -1) {
- zis[streamId].reset();
- }
- }
-
- private CMsgReader reader;
- private ZlibInStream[] zis;
- static LogWriter vlog = new LogWriter("TightDecoder");
-
- private static int convertByteArrayToInt(byte[] bytes) {
- return (bytes[0] << 32) | (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4];
- }
-
- private static byte[] convertIntToByteArray(int integer) {
- byte[] bytes = new byte[4];
- bytes[0] =(byte)( integer >> 24 );
- bytes[1] =(byte)( (integer << 8) >> 24 );
- bytes[2] =(byte)( (integer << 16) >> 24 );
- bytes[3] =(byte)( (integer << 24) >> 24 );
- return bytes;
- }
- private static int[] convertByteArrayToIntArray(byte[] bytes) {
- vlog.info("convertByteArrayToIntArray");
- ArrayList<Integer> integers = new ArrayList<Integer>();
- for (int index = 0; index < bytes.length; index += 4) {
- byte[] fourBytes = new byte[4];
- fourBytes[0] = bytes[index];
- fourBytes[1] = bytes[index+1];
- fourBytes[2] = bytes[index+2];
- fourBytes[3] = bytes[index+3];
- int integer = convertByteArrayToInt(fourBytes);
- integers.add(new Integer(integer));
- }
- int[] ints = new int[bytes.length/4];
- for (int index = 0; index < integers.size() ; index++) {
- ints[index] = (integers.get(index)).intValue();
- }
- return ints;
- }
-
- private static byte[] convertIntArrayToByteArray(int[] integers) {
- byte[] bytes = new byte[integers.length*4];
- for (int index = 0; index < integers.length; index++) {
- byte[] integerBytes = convertIntToByteArray(integers[index]);
- bytes[index*4] = integerBytes[0];
- bytes[1 + (index*4)] = integerBytes[1];
- bytes[2 + (index*4)] = integerBytes[2];
- bytes[3 + (index*4)] = integerBytes[3];
- }
- return bytes;
- }
-
- //
- // Decode data processed with the "Gradient" filter.
- //
-
- final private void FilterGradient24(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
-
- int x, y, c;
- int[] prevRow = new int[TIGHT_MAX_WIDTH * 3];
- int[] thisRow = new int[TIGHT_MAX_WIDTH * 3];
- int[] pix = new int[3];
- int[] est = new int[3];
-
- // Allocate netbuf and read in data
- int[] netbuf = new int[dataSize];
- for (int i = 0; i < dataSize; i++)
- netbuf[i] = is.readU8();
- //is.readBytes(netbuf, 0, dataSize);
-
- PixelFormat myFormat = handler.cp.pf();
- int rectHeight = r.height();
- int rectWidth = r.width();
-
- for (y = 0; y < rectHeight; y++) {
- /* First pixel in a row */
- for (c = 0; c < 3; c++) {
- pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
- thisRow[c] = pix[c];
- }
- if (myFormat.bigEndian) {
- buf[y*rectWidth] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
- } else {
- buf[y*rectWidth] =
- (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
- }
-
- /* Remaining pixels of a row */
- for (x = 1; x < rectWidth; x++) {
- for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 0xFF) {
- est[c] = 0xFF;
- } else if (est[c] < 0) {
- est[c] = 0;
- }
- pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
- thisRow[x*3+c] = pix[c];
- }
- if (myFormat.bigEndian) {
- buf[y*rectWidth] =
- (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
- } else {
- buf[y*rectWidth] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
- }
- }
-
- System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
- }
- }
-
- final private void FilterGradient(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) {
-
- int x, y, c;
- int[] prevRow = new int[TIGHT_MAX_WIDTH];
- int[] thisRow = new int[TIGHT_MAX_WIDTH];
- int[] pix = new int[3];
- int[] est = new int[3];
-
- // Allocate netbuf and read in data
- int[] netbuf = new int[dataSize];
- for (int i = 0; i < dataSize; i++)
- netbuf[i] = is.readU8();
- //is.readBytes(netbuf, 0, dataSize);
-
- PixelFormat myFormat = handler.cp.pf();
- int rectHeight = r.height();
- int rectWidth = r.width();
-
- for (y = 0; y < rectHeight; y++) {
- /* First pixel in a row */
- if (myFormat.bigEndian) {
- buf[y*rectWidth] =
- (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
- } else {
- buf[y*rectWidth] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
- }
- for (c = 0; c < 3; c++)
- pix[c] += prevRow[c];
-
- /* Remaining pixels of a row */
- for (x = 1; x < rectWidth; x++) {
- for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 255) {
- est[c] = 255;
- } else if (est[c] < 0) {
- est[c] = 0;
- }
- }
-
- // FIXME?
- System.arraycopy(pix, 0, netbuf, 0, netbuf.length);
- for (c = 0; c < 3; c++)
- pix[c] += est[c];
-
- System.arraycopy(thisRow, x*3, pix, 0, pix.length);
-
- if (myFormat.bigEndian) {
- buf[y*rectWidth+x] =
- (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24;
- } else {
- buf[y*rectWidth+x] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24;
- }
-
- }
-
- System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length);
- }
- }
-
- }
|