summaryrefslogtreecommitdiffstats
path: root/java/src/com/tigervnc/decoder/TightDecoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/tigervnc/decoder/TightDecoder.java')
-rw-r--r--java/src/com/tigervnc/decoder/TightDecoder.java525
1 files changed, 0 insertions, 525 deletions
diff --git a/java/src/com/tigervnc/decoder/TightDecoder.java b/java/src/com/tigervnc/decoder/TightDecoder.java
deleted file mode 100644
index 4e9969c7..00000000
--- a/java/src/com/tigervnc/decoder/TightDecoder.java
+++ /dev/null
@@ -1,525 +0,0 @@
-package com.tigervnc.decoder;
-
-import com.tigervnc.decoder.common.Repaintable;
-import com.tigervnc.vncviewer.RfbInputStream;
-import java.awt.Graphics;
-import java.awt.Color;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
-import java.awt.image.ImageObserver;
-import java.io.IOException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-//
-// Class that used for decoding Tight encoded data.
-//
-
-public class TightDecoder extends RawDecoder implements ImageObserver {
-
- final static int EncodingTight = 7;
-
- //
- // Tight decoder constants
- //
-
- final static int TightExplicitFilter = 0x04;
- final static int TightFill = 0x08;
- final static int TightJpeg = 0x09;
- final static int TightMaxSubencoding = 0x09;
- final static int TightFilterCopy = 0x00;
- final static int TightFilterPalette = 0x01;
- final static int TightFilterGradient = 0x02;
- final static int TightMinToCompress = 12;
-
- // Tight encoder's data.
- final static int tightZlibBufferSize = 512;
-
- public TightDecoder(Graphics g, RfbInputStream is) {
- super(g, is);
- tightInflaters = new Inflater[4];
- }
-
- public TightDecoder(Graphics g, RfbInputStream is, int frameBufferW,
- int frameBufferH) {
- super(g, is, frameBufferW, frameBufferH);
- tightInflaters = new Inflater[4];
- }
-
- //
- // Set and get methods for private TightDecoder
- //
-
- public void setRepainableControl(Repaintable r) {
- repainatableControl = r;
- }
-
- //
- // JPEG processing statistic methods
- //
-
- public long getNumJPEGRects() {
- return statNumRectsTightJPEG;
- }
-
- public void setNumJPEGRects(int v) {
- statNumRectsTightJPEG = v;
- }
-
- //
- // Tight processing statistic methods
- //
-
- public long getNumTightRects() {
- return statNumRectsTight;
- }
-
- public void setNumTightRects(int v) {
- statNumRectsTight = v;
- }
-
- //
- // Handle a Tight-encoded rectangle.
- //
-
- public void handleRect(int x, int y, int w, int h) throws Exception {
-
- //
- // Write encoding ID to record output stream
- //
-
- if (dos != null) {
- dos.writeInt(TightDecoder.EncodingTight);
- }
-
- int comp_ctl = rfbis.readU8();
-
- if (dos != null) {
- // Tell the decoder to flush each of the four zlib streams.
- dos.writeByte(comp_ctl | 0x0F);
- }
-
- // Flush zlib streams if we are told by the server to do so.
- for (int stream_id = 0; stream_id < 4; stream_id++) {
- if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
- tightInflaters[stream_id] = null;
- }
- comp_ctl >>= 1;
- }
-
- // Check correctness of subencoding value.
- if (comp_ctl > TightDecoder.TightMaxSubencoding) {
- throw new Exception("Incorrect tight subencoding: " + comp_ctl);
- }
-
- // Handle solid-color rectangles.
- if (comp_ctl == TightDecoder.TightFill) {
-
- if (bytesPerPixel == 1) {
- int idx = rfbis.readU8();
- graphics.setColor(getColor256()[idx]);
- if (dos != null) {
- dos.writeByte(idx);
- }
- } else {
- byte[] buf = new byte[3];
- rfbis.readFully(buf);
- if (dos != null) {
- dos.write(buf);
- }
- Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 |
- (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
- graphics.setColor(bg);
- }
- graphics.fillRect(x, y, w, h);
- repainatableControl.scheduleRepaint(x, y, w, h);
- return;
-
- }
-
- if (comp_ctl == TightDecoder.TightJpeg) {
-
- statNumRectsTightJPEG++;
-
- // Read JPEG data.
- byte[] jpegData = new byte[rfbis.readCompactLen()];
- rfbis.readFully(jpegData);
- if (dos != null) {
- recordCompactLen(jpegData.length);
- dos.write(jpegData);
- }
-
- // Create an Image object from the JPEG data.
- Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
-
- // Remember the rectangle where the image should be drawn.
- jpegRect = new Rectangle(x, y, w, h);
-
- // Let the imageUpdate() method do the actual drawing, here just
- // wait until the image is fully loaded and drawn.
- synchronized(jpegRect) {
- Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
- try {
- // Wait no longer than three seconds.
- jpegRect.wait(3000);
- } catch (InterruptedException e) {
- throw new Exception("Interrupted while decoding JPEG image");
- }
- }
-
- // Done, jpegRect is not needed any more.
- jpegRect = null;
- return;
-
- } else {
- statNumRectsTight++;
- }
-
- // Read filter id and parameters.
- int numColors = 0, rowSize = w;
- byte[] palette8 = new byte[2];
- int[] palette24 = new int[256];
- boolean useGradient = false;
- if ((comp_ctl & TightDecoder.TightExplicitFilter) != 0) {
- int filter_id = rfbis.readU8();
- if (dos != null) {
- dos.writeByte(filter_id);
- }
- if (filter_id == TightDecoder.TightFilterPalette) {
- numColors = rfbis.readU8() + 1;
- if (dos != null) {
- dos.writeByte((numColors - 1));
- }
- if (bytesPerPixel == 1) {
- if (numColors != 2) {
- throw new Exception("Incorrect tight palette size: " + numColors);
- }
- rfbis.readFully(palette8);
- if (dos != null) {
- dos.write(palette8);
- }
- } else {
- byte[] buf = new byte[numColors * 3];
- rfbis.readFully(buf);
- if (dos != null) {
- dos.write(buf);
- }
- for (int i = 0; i < numColors; i++) {
- palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
- (buf[i * 3 + 1] & 0xFF) << 8 |
- (buf[i * 3 + 2] & 0xFF));
- }
- }
- if (numColors == 2) {
- rowSize = (w + 7) / 8;
- }
- } else if (filter_id == TightDecoder.TightFilterGradient) {
- useGradient = true;
- } else if (filter_id != TightDecoder.TightFilterCopy) {
- throw new Exception("Incorrect tight filter id: " + filter_id);
- }
- }
- if (numColors == 0 && bytesPerPixel == 4)
- rowSize *= 3;
-
- // Read, optionally uncompress and decode data.
- int dataSize = h * rowSize;
- if (dataSize < TightDecoder.TightMinToCompress) {
- // Data size is small - not compressed with zlib.
- if (numColors != 0) {
- // Indexed colors.
- byte[] indexedData = new byte[dataSize];
- rfbis.readFully(indexedData);
- if (dos != null) {
- dos.write(indexedData);
- }
- if (numColors == 2) {
- // Two colors.
- if (bytesPerPixel == 1) {
- decodeMonoData(x, y, w, h, indexedData, palette8);
- } else {
- decodeMonoData(x, y, w, h, indexedData, palette24);
- }
- } else {
- // 3..255 colors (assuming bytesPixel == 4).
- int i = 0;
- for (int dy = y; dy < y + h; dy++) {
- for (int dx = x; dx < x + w; dx++) {
- pixels24[dy * framebufferWidth + dx] =
- palette24[indexedData[i++] & 0xFF];
- }
- }
- }
- } else if (useGradient) {
- // "Gradient"-processed data
- byte[] buf = new byte[w * h * 3];
- rfbis.readFully(buf);
- if (dos != null) {
- dos.write(buf);
- }
- decodeGradientData(x, y, w, h, buf);
- } else {
- // Raw truecolor data.
- if (bytesPerPixel == 1) {
- for (int dy = y; dy < y + h; dy++) {
- rfbis.readFully(pixels8, dy * framebufferWidth + x, w);
- if (dos != null) {
- dos.write(pixels8, dy * framebufferWidth + x, w);
- }
- }
- } else {
- byte[] buf = new byte[w * 3];
- int i, offset;
- for (int dy = y; dy < y + h; dy++) {
- rfbis.readFully(buf);
- if (dos != null) {
- dos.write(buf);
- }
- offset = dy * framebufferWidth + x;
- for (i = 0; i < w; i++) {
- pixels24[offset + i] =
- (buf[i * 3] & 0xFF) << 16 |
- (buf[i * 3 + 1] & 0xFF) << 8 |
- (buf[i * 3 + 2] & 0xFF);
- }
- }
- }
- }
- } else {
- // Data was compressed with zlib.
- int zlibDataLen = rfbis.readCompactLen();
- byte[] zlibData = new byte[zlibDataLen];
- rfbis.readFully(zlibData);
- int stream_id = comp_ctl & 0x03;
- if (tightInflaters[stream_id] == null) {
- tightInflaters[stream_id] = new Inflater();
- }
- Inflater myInflater = tightInflaters[stream_id];
- myInflater.setInput(zlibData);
- byte[] buf = new byte[dataSize];
- myInflater.inflate(buf);
- if (dos != null) {
- recordCompressedData(buf);
- }
-
- if (numColors != 0) {
- // Indexed colors.
- if (numColors == 2) {
- // Two colors.
- if (bytesPerPixel == 1) {
- decodeMonoData(x, y, w, h, buf, palette8);
- } else {
- decodeMonoData(x, y, w, h, buf, palette24);
- }
- } else {
- // More than two colors (assuming bytesPixel == 4).
- int i = 0;
- for (int dy = y; dy < y + h; dy++) {
- for (int dx = x; dx < x + w; dx++) {
- pixels24[dy * framebufferWidth + dx] =
- palette24[buf[i++] & 0xFF];
- }
- }
- }
- } else if (useGradient) {
- // Compressed "Gradient"-filtered data (assuming bytesPixel == 4).
- decodeGradientData(x, y, w, h, buf);
- } else {
- // Compressed truecolor data.
- if (bytesPerPixel == 1) {
- int destOffset = y * framebufferWidth + x;
- for (int dy = 0; dy < h; dy++) {
- System.arraycopy(buf, dy * w, pixels8, destOffset, w);
- destOffset += framebufferWidth;
- }
- } else {
- int srcOffset = 0;
- int destOffset, i;
- for (int dy = 0; dy < h; dy++) {
- myInflater.inflate(buf);
- destOffset = (y + dy) * framebufferWidth + x;
- for (i = 0; i < w; i++) {
- RawDecoder.pixels24[destOffset + i] =
- (buf[srcOffset] & 0xFF) << 16 |
- (buf[srcOffset + 1] & 0xFF) << 8 |
- (buf[srcOffset + 2] & 0xFF);
- srcOffset += 3;
- }
- }
- }
- }
- }
- handleUpdatedPixels(x, y, w, h);
- }
-
- //
- // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
- //
-
- private void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
-
- int dx, dy, n;
- int i = y * framebufferWidth + x;
- int rowBytes = (w + 7) / 8;
- byte b;
-
- for (dy = 0; dy < h; dy++) {
- for (dx = 0; dx < w / 8; dx++) {
- b = src[dy*rowBytes+dx];
- for (n = 7; n >= 0; n--)
- pixels8[i++] = palette[b >> n & 1];
- }
- for (n = 7; n >= 8 - w % 8; n--) {
- pixels8[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
- }
- i += (framebufferWidth - w);
- }
- }
-
- private void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
-
- int dx, dy, n;
- int i = y * framebufferWidth + x;
- int rowBytes = (w + 7) / 8;
- byte b;
-
- for (dy = 0; dy < h; dy++) {
- for (dx = 0; dx < w / 8; dx++) {
- b = src[dy*rowBytes+dx];
- for (n = 7; n >= 0; n--)
- pixels24[i++] = palette[b >> n & 1];
- }
- for (n = 7; n >= 8 - w % 8; n--) {
- pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
- }
- i += (framebufferWidth - w);
- }
- }
-
- //
- // Decode data processed with the "Gradient" filter.
- //
-
- private void decodeGradientData (int x, int y, int w, int h, byte[] buf) {
-
- int dx, dy, c;
- byte[] prevRow = new byte[w * 3];
- byte[] thisRow = new byte[w * 3];
- byte[] pix = new byte[3];
- int[] est = new int[3];
-
- int offset = y * framebufferWidth + x;
-
- for (dy = 0; dy < h; dy++) {
-
- /* First pixel in a row */
- for (c = 0; c < 3; c++) {
- pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
- thisRow[c] = pix[c];
- }
- pixels24[offset++] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
-
- /* Remaining pixels of a row */
- for (dx = 1; dx < w; dx++) {
- for (c = 0; c < 3; c++) {
- est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
- (prevRow[(dx-1) * 3 + c] & 0xFF));
- if (est[c] > 0xFF) {
- est[c] = 0xFF;
- } else if (est[c] < 0x00) {
- est[c] = 0x00;
- }
- pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
- thisRow[dx * 3 + c] = pix[c];
- }
- pixels24[offset++] =
- (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
- }
-
- System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
- offset += (framebufferWidth - w);
- }
- }
-
- //
- // Override the ImageObserver interface method to handle drawing of
- // JPEG-encoded data.
- //
-
- public boolean imageUpdate(Image img, int infoflags,
- int x, int y, int width, int height) {
- if ((infoflags & (ALLBITS | ABORT)) == 0) {
- return true; // We need more image data.
- } else {
- // If the whole image is available, draw it now.
- if ((infoflags & ALLBITS) != 0) {
- if (jpegRect != null) {
- synchronized(jpegRect) {
- graphics.drawImage(img, jpegRect.x, jpegRect.y, null);
- repainatableControl.scheduleRepaint(jpegRect.x, jpegRect.y,
- jpegRect.width, jpegRect.height);
- jpegRect.notify();
- }
- }
- }
- return false; // All image data was processed.
- }
- }
-
- //
- // Write an integer in compact representation (1..3 bytes) into the
- // recorded session file.
- //
-
- void recordCompactLen(int len) throws IOException {
- byte[] buf = new byte[3];
- int bytes = 0;
- buf[bytes++] = (byte)(len & 0x7F);
- if (len > 0x7F) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (byte)(len >> 7 & 0x7F);
- if (len > 0x3FFF) {
- buf[bytes-1] |= 0x80;
- buf[bytes++] = (byte)(len >> 14 & 0xFF);
- }
- }
- if (dos != null) dos.write(buf, 0, bytes);
- }
-
- //
- // Compress and write the data into the recorded session file.
- //
-
- void recordCompressedData(byte[] data, int off, int len) throws IOException {
- Deflater deflater = new Deflater();
- deflater.setInput(data, off, len);
- int bufSize = len + len / 100 + 12;
- byte[] buf = new byte[bufSize];
- deflater.finish();
- int compressedSize = deflater.deflate(buf);
- recordCompactLen(compressedSize);
- if (dos != null) dos.write(buf, 0, compressedSize);
- }
-
- void recordCompressedData(byte[] data) throws IOException {
- recordCompressedData(data, 0, data.length);
- }
-
- //
- // Private members
- //
-
- private Inflater[] tightInflaters;
- // Since JPEG images are loaded asynchronously, we have to remember
- // their position in the framebuffer. Also, this jpegRect object is
- // used for synchronization between the rfbThread and a JVM's thread
- // which decodes and loads JPEG images.
- private Rectangle jpegRect;
- private Repaintable repainatableControl = null;
- // Jpeg decoding statistics
- private long statNumRectsTightJPEG = 0;
- // Tight decoding statistics
- private long statNumRectsTight = 0;
-}