summaryrefslogtreecommitdiffstats
path: root/java/src/com/tigervnc/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/tigervnc/decoder')
-rw-r--r--java/src/com/tigervnc/decoder/CoRREDecoder.java85
-rw-r--r--java/src/com/tigervnc/decoder/CopyRectDecoder.java50
-rw-r--r--java/src/com/tigervnc/decoder/HextileDecoder.java228
-rw-r--r--java/src/com/tigervnc/decoder/RREDecoder.java85
-rw-r--r--java/src/com/tigervnc/decoder/RawDecoder.java223
-rw-r--r--java/src/com/tigervnc/decoder/TightDecoder.java525
-rw-r--r--java/src/com/tigervnc/decoder/ZRLEDecoder.java310
-rw-r--r--java/src/com/tigervnc/decoder/ZlibDecoder.java103
-rw-r--r--java/src/com/tigervnc/decoder/common/Repaintable.java7
9 files changed, 1616 insertions, 0 deletions
diff --git a/java/src/com/tigervnc/decoder/CoRREDecoder.java b/java/src/com/tigervnc/decoder/CoRREDecoder.java
new file mode 100644
index 00000000..bc086686
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/CoRREDecoder.java
@@ -0,0 +1,85 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.awt.Graphics;
+import java.awt.Color;
+import java.io.IOException;
+
+//
+// Class that used for decoding CoRRE encoded data.
+//
+
+public class CoRREDecoder extends RawDecoder {
+
+ final static int EncodingCoRRE = 4;
+
+ public CoRREDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public CoRREDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Override handleRect method to decode CoRRE encoded data insted of
+ // raw pixel data.
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(CoRREDecoder.EncodingCoRRE);
+ }
+
+ int nSubrects = rfbis.readU32();
+
+ byte[] bg_buf = new byte[bytesPerPixel];
+ rfbis.readFully(bg_buf);
+ Color pixel;
+ if (bytesPerPixel == 1) {
+ pixel = getColor256()[bg_buf[0] & 0xFF];
+ } else {
+ pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
+ }
+ graphics.setColor(pixel);
+ graphics.fillRect(x, y, w, h);
+
+ byte[] buf = new byte[nSubrects * (bytesPerPixel + 4)];
+ rfbis.readFully(buf);
+
+ //
+ // Save decoded data to data output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(nSubrects);
+ dos.write(bg_buf);
+ dos.write(buf);
+ }
+
+ int sx, sy, sw, sh;
+ int i = 0;
+
+ for (int j = 0; j < nSubrects; j++) {
+ if (bytesPerPixel == 1) {
+ pixel = getColor256()[buf[i++] & 0xFF];
+ } else {
+ pixel = new Color(buf[i+2] & 0xFF, buf[i+1] & 0xFF, buf[i] & 0xFF);
+ i += 4;
+ }
+ sx = x + (buf[i++] & 0xFF);
+ sy = y + (buf[i++] & 0xFF);
+ sw = buf[i++] & 0xFF;
+ sh = buf[i++] & 0xFF;
+
+ graphics.setColor(pixel);
+ graphics.fillRect(sx, sy, sw, sh);
+ }
+ }
+}
diff --git a/java/src/com/tigervnc/decoder/CopyRectDecoder.java b/java/src/com/tigervnc/decoder/CopyRectDecoder.java
new file mode 100644
index 00000000..07a14bdb
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/CopyRectDecoder.java
@@ -0,0 +1,50 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.awt.Graphics;
+import java.io.IOException;
+
+//
+// Class that used for decoding CopyRect encoded data.
+//
+
+public class CopyRectDecoder extends RawDecoder {
+
+ final static int EncodingCopyRect = 1;
+
+ public CopyRectDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public CopyRectDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Override handleRect method handle CopyRect
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(CopyRectDecoder.EncodingCopyRect);
+ }
+
+ int copyRectSrcX = rfbis.readU16();
+ int copyRectSrcY = rfbis.readU16();
+
+ // If the session is being recorded:
+ if (dos != null) {
+ dos.writeShort(copyRectSrcX);
+ dos.writeShort(copyRectSrcY);
+ }
+
+ graphics.copyArea(copyRectSrcX, copyRectSrcY, w, h,
+ x - copyRectSrcX, y - copyRectSrcY);
+ }
+}
diff --git a/java/src/com/tigervnc/decoder/HextileDecoder.java b/java/src/com/tigervnc/decoder/HextileDecoder.java
new file mode 100644
index 00000000..da7e7781
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/HextileDecoder.java
@@ -0,0 +1,228 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.decoder.common.Repaintable;
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.io.IOException;
+
+//
+// Class that used for decoding hextile encoded data.
+//
+
+public class HextileDecoder extends RawDecoder {
+
+ final static int EncodingHextile = 5;
+
+ // Contstants used in the Hextile decoder
+ final static int
+ HextileRaw = 1,
+ HextileBackgroundSpecified = 2,
+ HextileForegroundSpecified = 4,
+ HextileAnySubrects = 8,
+ HextileSubrectsColoured = 16;
+
+ public HextileDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public HextileDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Set private members methods
+ //
+
+ public void setRepainableControl(Repaintable r) {
+ repainableControl = r;
+ }
+
+ //
+ // Override handleRect method to decode Hextile encoded data insted of
+ // raw pixel data.
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException,
+ Exception {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(HextileDecoder.EncodingHextile);
+ }
+
+ hextile_bg = new Color(0);
+ hextile_fg = new Color(0);
+
+ for (int ty = y; ty < y + h; ty += 16) {
+ int th = 16;
+ if (y + h - ty < 16)
+ th = y + h - ty;
+
+ for (int tx = x; tx < x + w; tx += 16) {
+ int tw = 16;
+ if (x + w - tx < 16)
+ tw = x + w - tx;
+
+ handleHextileSubrect(tx, ty, tw, th);
+ }
+ if (repainableControl != null)
+ repainableControl.scheduleRepaint(x, y, w, h);
+ }
+ if (repainableControl != null)
+ repainableControl.scheduleRepaint(x, y, w, h);
+ }
+
+ //
+ // Handle one tile in the Hextile-encoded data.
+ //
+
+ private void handleHextileSubrect(int tx, int ty, int tw, int th)
+ throws IOException, Exception {
+
+ int subencoding = rfbis.readU8();
+
+ //
+ // Save decoded data to data output stream
+ //
+
+ if (dos != null) {
+ dos.writeByte((byte)subencoding);
+ }
+
+ // Is it a raw-encoded sub-rectangle?
+ if ((subencoding & HextileRaw) != 0) {
+ //
+ // Disable encoding id writting to record stream
+ // in super (RawDecoder) class, cause we write subencoding ID
+ // in this class (see code above).
+ //
+
+ super.enableEncodingRecordWritting(false);
+ super.handleRect(tx, ty, tw, th);
+ super.handleUpdatedPixels(tx, ty, tw, th);
+ super.enableEncodingRecordWritting(true);
+ return;
+ }
+
+ // Read and draw the background if specified.
+ byte[] cbuf = new byte[bytesPerPixel];
+ if ((subencoding & HextileBackgroundSpecified) != 0) {
+ rfbis.readFully(cbuf);
+ if (bytesPerPixel == 1) {
+ hextile_bg = getColor256()[cbuf[0] & 0xFF];
+ } else {
+ hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF);
+ }
+
+ //
+ // Save decoded data to data output stream
+ //
+
+ if (dos != null) {
+ dos.write(cbuf);
+ }
+ }
+ graphics.setColor(hextile_bg);
+ graphics.fillRect(tx, ty, tw, th);
+
+ // Read the foreground color if specified.
+ if ((subencoding & HextileForegroundSpecified) != 0) {
+ rfbis.readFully(cbuf);
+ if (bytesPerPixel == 1) {
+ hextile_fg = getColor256()[cbuf[0] & 0xFF];
+ } else {
+ hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF);
+ }
+
+ //
+ // Save decoded data to data output stream
+ //
+
+ if (dos != null) {
+ dos.write(cbuf);
+ }
+ }
+
+ // Done with this tile if there is no sub-rectangles.
+ if ((subencoding & HextileAnySubrects) == 0)
+ return;
+
+ int nSubrects = rfbis.readU8();
+ int bufsize = nSubrects * 2;
+ if ((subencoding & HextileSubrectsColoured) != 0) {
+ bufsize += nSubrects * bytesPerPixel;
+ }
+ byte[] buf = new byte[bufsize];
+ rfbis.readFully(buf);
+
+ //
+ // Save decoded data to data output stream
+ //
+
+ if (dos != null) {
+ dos.writeByte((byte)nSubrects);
+ dos.write(buf);
+ }
+
+ int b1, b2, sx, sy, sw, sh;
+ int i = 0;
+
+ if ((subencoding & HextileSubrectsColoured) == 0) {
+
+ // Sub-rectangles are all of the same color.
+ graphics.setColor(hextile_fg);
+ for (int j = 0; j < nSubrects; j++) {
+ b1 = buf[i++] & 0xFF;
+ b2 = buf[i++] & 0xFF;
+ sx = tx + (b1 >> 4);
+ sy = ty + (b1 & 0xf);
+ sw = (b2 >> 4) + 1;
+ sh = (b2 & 0xf) + 1;
+ graphics.fillRect(sx, sy, sw, sh);
+ }
+ } else if (bytesPerPixel == 1) {
+
+ // BGR233 (8-bit color) version for colored sub-rectangles.
+ for (int j = 0; j < nSubrects; j++) {
+ hextile_fg = getColor256()[buf[i++] & 0xFF];
+ b1 = buf[i++] & 0xFF;
+ b2 = buf[i++] & 0xFF;
+ sx = tx + (b1 >> 4);
+ sy = ty + (b1 & 0xf);
+ sw = (b2 >> 4) + 1;
+ sh = (b2 & 0xf) + 1;
+ graphics.setColor(hextile_fg);
+ graphics.fillRect(sx, sy, sw, sh);
+ }
+
+ } else {
+
+ // Full-color (24-bit) version for colored sub-rectangles.
+ for (int j = 0; j < nSubrects; j++) {
+ hextile_fg = new Color(buf[i+2] & 0xFF,
+ buf[i+1] & 0xFF,
+ buf[i] & 0xFF);
+ i += 4;
+ b1 = buf[i++] & 0xFF;
+ b2 = buf[i++] & 0xFF;
+ sx = tx + (b1 >> 4);
+ sy = ty + (b1 & 0xf);
+ sw = (b2 >> 4) + 1;
+ sh = (b2 & 0xf) + 1;
+ graphics.setColor(hextile_fg);
+ graphics.fillRect(sx, sy, sw, sh);
+ }
+
+ }
+ }
+
+ // These colors should be kept between handleHextileSubrect() calls.
+ private Color hextile_bg, hextile_fg;
+ // Repaitable object
+ private Repaintable repainableControl = null;
+}
diff --git a/java/src/com/tigervnc/decoder/RREDecoder.java b/java/src/com/tigervnc/decoder/RREDecoder.java
new file mode 100644
index 00000000..02eb513f
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/RREDecoder.java
@@ -0,0 +1,85 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.awt.Graphics;
+import java.awt.Color;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+
+//
+// Class that used for decoding RRE encoded data.
+//
+
+public class RREDecoder extends RawDecoder {
+
+ final static int EncodingRRE = 2;
+
+ public RREDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public RREDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Override handleRect method to decode RRE encoded data insted of
+ // raw pixel data.
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(RREDecoder.EncodingRRE);
+ }
+
+ int nSubrects = rfbis.readU32();
+ byte[] bg_buf = new byte[bytesPerPixel];
+ rfbis.readFully(bg_buf);
+ Color pixel;
+ if (bytesPerPixel == 1) {
+ pixel = getColor256()[bg_buf[0] & 0xFF];
+ } else {
+ pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
+ }
+ graphics.setColor(pixel);
+ graphics.fillRect(x, y, w, h);
+ byte[] buf = new byte[nSubrects * (bytesPerPixel + 8)];
+ rfbis.readFully(buf);
+ DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
+
+ //
+ // Save decoded data to data output stream
+ //
+ if (dos != null) {
+ dos.writeInt(nSubrects);
+ dos.write(bg_buf);
+ dos.write(buf);
+ }
+
+ int sx, sy, sw, sh;
+ for (int j = 0; j < nSubrects; j++) {
+ if (bytesPerPixel == 1) {
+ pixel = getColor256()[ds.readUnsignedByte()];
+ } else {
+ ds.skip(4);
+ pixel = new Color(buf[j*12+2] & 0xFF,
+ buf[j*12+1] & 0xFF,
+ buf[j*12] & 0xFF);
+ }
+ sx = x + ds.readUnsignedShort();
+ sy = y + ds.readUnsignedShort();
+ sw = ds.readUnsignedShort();
+ sh = ds.readUnsignedShort();
+
+ graphics.setColor(pixel);
+ graphics.fillRect(sx, sy, sw, sh);
+ }
+ }
+}
diff --git a/java/src/com/tigervnc/decoder/RawDecoder.java b/java/src/com/tigervnc/decoder/RawDecoder.java
new file mode 100644
index 00000000..9ef167a7
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/RawDecoder.java
@@ -0,0 +1,223 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.io.IOException;
+import java.io.DataOutput;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.ColorModel;
+import java.awt.image.DirectColorModel;
+import java.awt.image.MemoryImageSource;
+import java.awt.Color;
+import java.awt.Toolkit;
+
+//
+// This is base decoder class.
+// Other classes will be childs of RawDecoder.
+//
+
+public class RawDecoder {
+ final static int EncodingRaw = 0;
+
+ public RawDecoder(Graphics g, RfbInputStream is) {
+ setGraphics(g);
+ setRfbInputStream(is);
+ }
+
+ public RawDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ setGraphics(g);
+ setRfbInputStream(is);
+ setFrameBufferSize(frameBufferW, frameBufferH);
+ // FIXME: cm24 created in getColorModel24.
+ // Remove if no bugs
+ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+ }
+
+ //
+ // Set methods to set value of non-static protected members of class
+ //
+
+ public void setRfbInputStream(RfbInputStream is) {
+ rfbis = is;
+ }
+
+ public void setGraphics(Graphics g) {
+ graphics = g;
+ }
+
+ public void setBPP(int bpp) {
+ bytesPerPixel = bpp;
+ }
+
+ public void setFrameBufferSize(int w, int h) {
+ framebufferWidth = w;
+ framebufferHeight = h;
+ }
+
+ //
+ // FIXME: Rename this method after we don't need RecordInterface
+ // in RawDecoder class to record session
+ //
+
+ public void setDataOutputStream(DataOutput os) {
+ dos = os;
+ }
+
+ //
+ // Decodes Raw Pixels data and draw it into graphics
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException, Exception {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if ((dos != null) && (enableEncodingRecordWritting)) {
+ dos.writeInt(RawDecoder.EncodingRaw);
+ }
+
+ if (bytesPerPixel == 1) {
+ for (int dy = y; dy < y + h; dy++) {
+ if (pixels8 != null) {
+ rfbis.readFully(pixels8, dy * framebufferWidth + x, w);
+ }
+ //
+ // Save decoded data to record output stream
+ //
+ if (dos != null) {
+ dos.write(pixels8, dy * framebufferWidth + x, w);
+ }
+ }
+ } else {
+ byte[] buf = new byte[w * 4];
+ int i, offset;
+ for (int dy = y; dy < y + h; dy++) {
+ rfbis.readFully(buf);
+ //
+ // Save decoded data to record output stream
+ //
+ if (dos != null) {
+ dos.write(buf);
+ }
+ offset = dy * framebufferWidth + x;
+ if (pixels24 != null) {
+ for (i = 0; i < w; i++) {
+ pixels24[offset + i] =
+ (buf[i * 4 + 2] & 0xFF) << 16 |
+ (buf[i * 4 + 1] & 0xFF) << 8 |
+ (buf[i * 4] & 0xFF);
+ } //for
+ } // if
+ } // for
+ } // else
+ handleUpdatedPixels(x, y, w, h);
+ } // void
+
+ //
+ // Display newly updated area of pixels.
+ //
+
+ protected void handleUpdatedPixels(int x, int y, int w, int h) {
+ // Draw updated pixels of the off-screen image.
+ pixelsSource.newPixels(x, y, w, h);
+ graphics.setClip(x, y, w, h);
+ graphics.drawImage(rawPixelsImage, 0, 0, null);
+ graphics.setClip(0, 0, framebufferWidth, framebufferHeight);
+ }
+
+ //
+ // Updates pixels data.
+ // This method must be called when framebuffer is resized
+ // or BPP is changed.
+ //
+
+ public void update() {
+ // Images with raw pixels should be re-allocated on every change
+ // of geometry or pixel format.
+ int fbWidth = framebufferWidth;
+ int fbHeight = framebufferHeight;
+
+ if (bytesPerPixel == 1) {
+ pixels24 = null;
+ pixels8 = new byte[fbWidth * fbHeight];
+ pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(),
+ pixels8, 0, fbWidth);
+ } else {
+ pixels8 = null;
+ pixels24 = new int[fbWidth * fbHeight];
+ pixelsSource =
+ new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
+ }
+ pixelsSource.setAnimated(true);
+ rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
+ }
+
+ //
+ // Private static members access methods
+ //
+
+ protected ColorModel getColorModel8() {
+ if (cm8 == null) {
+ cm8 = cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+ }
+ return cm8;
+ }
+
+ protected ColorModel getColorModel24() {
+ if (cm24 == null) {
+ cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+ }
+ return cm24;
+ }
+
+ protected Color[]getColor256() {
+ if (color256 == null) {
+ color256 = new Color[256];
+ for (int i = 0; i < 256; i++)
+ color256[i] = new Color(cm8.getRGB(i));
+ }
+ return color256;
+ }
+
+ //
+ // This method will be used by HextileDecoder to disable
+ // double writting encoding id to record stream.
+ //
+ // FIXME: Try to find better solution than this.
+ //
+
+ protected void enableEncodingRecordWritting(boolean enable) {
+ enableEncodingRecordWritting = enable;
+ }
+
+ //
+ // Unique data for every decoder (? maybe not ?)
+ //
+
+ protected int bytesPerPixel = 4;
+ protected int framebufferWidth = 0;
+ protected int framebufferHeight = 0;
+ protected RfbInputStream rfbis = null;
+ protected Graphics graphics = null;
+ protected DataOutput dos = null;
+ protected boolean enableEncodingRecordWritting = true;
+
+ //
+ // This data must be shared between decoders
+ //
+
+ protected static byte []pixels8 = null;
+ protected static int []pixels24 = null;
+ protected static MemoryImageSource pixelsSource = null;
+ protected static Image rawPixelsImage = null;
+
+ //
+ // Access to this static members only though protected methods
+ //
+
+ private static ColorModel cm8 = null;
+ private static ColorModel cm24 = null;
+ private static Color []color256 = null;
+}
diff --git a/java/src/com/tigervnc/decoder/TightDecoder.java b/java/src/com/tigervnc/decoder/TightDecoder.java
new file mode 100644
index 00000000..015f73cd
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/TightDecoder.java
@@ -0,0 +1,525 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.decoder.common.Repaintable;
+import com.tightvnc.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;
+}
diff --git a/java/src/com/tigervnc/decoder/ZRLEDecoder.java b/java/src/com/tigervnc/decoder/ZRLEDecoder.java
new file mode 100644
index 00000000..a22da908
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/ZRLEDecoder.java
@@ -0,0 +1,310 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.InStream;
+import com.tightvnc.vncviewer.RfbInputStream;
+import com.tightvnc.vncviewer.ZlibInStream;
+import java.awt.Graphics;
+import com.tightvnc.vncviewer.MemInStream;
+import java.awt.Color;
+import java.awt.Toolkit;
+import java.awt.image.MemoryImageSource;
+import java.io.IOException;
+
+//
+// Class that used for decoding ZRLE encoded data.
+//
+
+public class ZRLEDecoder extends RawDecoder {
+
+ final static int EncodingZRLE = 16;
+
+ public ZRLEDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public ZRLEDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Handle a ZRLE-encoded rectangle.
+ //
+ // FIXME: Currently, session recording is not fully supported for ZRLE.
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException, Exception {
+
+ //
+ // Write encoding ID to record output stream
+ //
+
+ if (dos != null) {
+ dos.writeInt(ZRLEDecoder.EncodingZRLE);
+ }
+
+ if (zrleInStream == null)
+ zrleInStream = new ZlibInStream();
+
+ int nBytes = rfbis.readU32();
+ if (nBytes > 64 * 1024 * 1024)
+ throw new Exception("ZRLE decoder: illegal compressed data size");
+
+ if (zrleBuf == null || zrleBufLen < nBytes) {
+ zrleBufLen = nBytes + 4096;
+ zrleBuf = new byte[zrleBufLen];
+ }
+
+ // FIXME: Do not wait for all the data before decompression.
+ rfbis.readFully(zrleBuf, 0, nBytes);
+
+ //
+ // Override handleRect method to decode RRE encoded data insted of
+ // raw pixel data.
+ //
+
+ if (dos != null) {
+ if (!zrleRecWarningShown) {
+ System.out.println("Warning: ZRLE session can be recorded" +
+ " only from the beginning");
+ System.out.println("Warning: Recorded file may be corrupted");
+ zrleRecWarningShown = true;
+ }
+ }
+
+ zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
+
+ for (int ty = y; ty < y+h; ty += 64) {
+
+ int th = Math.min(y+h-ty, 64);
+
+ for (int tx = x; tx < x+w; tx += 64) {
+
+ int tw = Math.min(x+w-tx, 64);
+
+ int mode = zrleInStream.readU8();
+ boolean rle = (mode & 128) != 0;
+ int palSize = mode & 127;
+ int[] palette = new int[128];
+
+ readZrlePalette(palette, palSize);
+
+ if (palSize == 1) {
+ int pix = palette[0];
+ Color c = (bytesPerPixel == 1) ?
+ getColor256()[pix] : new Color(0xFF000000 | pix);
+ graphics.setColor(c);
+ graphics.fillRect(tx, ty, tw, th);
+ continue;
+ }
+
+ if (!rle) {
+ if (palSize == 0) {
+ readZrleRawPixels(tw, th);
+ } else {
+ readZrlePackedPixels(tw, th, palette, palSize);
+ }
+ } else {
+ if (palSize == 0) {
+ readZrlePlainRLEPixels(tw, th);
+ } else {
+ readZrlePackedRLEPixels(tw, th, palette);
+ }
+ }
+ handleUpdatedZrleTile(tx, ty, tw, th);
+ }
+ }
+ zrleInStream.reset();
+ }
+
+ //
+ // Override update() method cause we have own data that
+ // must be updated when framebuffer is resized or BPP is changed
+ //
+
+ public void update() {
+ // Images with raw pixels should be re-allocated on every change
+ // of geometry or pixel format.
+ int fbWidth = framebufferWidth;
+ int fbHeight = framebufferHeight;
+
+ if (bytesPerPixel == 1) {
+ RawDecoder.pixels24 = null;
+ RawDecoder.pixels8 = new byte[fbWidth * fbHeight];
+ RawDecoder.pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(), pixels8, 0, fbWidth);
+ zrleTilePixels24 = null;
+ zrleTilePixels8 = new byte[64 * 64];
+ } else {
+ RawDecoder.pixels8 = null;
+ RawDecoder.pixels24 = new int[fbWidth * fbHeight];
+ RawDecoder.pixelsSource =
+ new MemoryImageSource(fbWidth, fbHeight, getColorModel24(), pixels24, 0, fbWidth);
+ zrleTilePixels8 = null;
+ zrleTilePixels24 = new int[64 * 64];
+ }
+ RawDecoder.pixelsSource.setAnimated(true);
+ RawDecoder.rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
+ }
+
+ //
+ // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
+ //
+
+ private void handleUpdatedZrleTile(int x, int y, int w, int h) {
+ Object src, dst;
+ if (bytesPerPixel == 1) {
+ src = zrleTilePixels8; dst = pixels8;
+ } else {
+ src = zrleTilePixels24; dst = pixels24;
+ }
+ int offsetSrc = 0;
+ int offsetDst = (y * framebufferWidth + x);
+ for (int j = 0; j < h; j++) {
+ System.arraycopy(src, offsetSrc, dst, offsetDst, w);
+ offsetSrc += w;
+ offsetDst += framebufferWidth;
+ }
+ handleUpdatedPixels(x, y, w, h);
+ }
+
+ //
+ // Private methods for reading ZRLE data
+ //
+
+ private int readPixel(InStream is) throws Exception {
+ int pix;
+ if (bytesPerPixel == 1) {
+ pix = is.readU8();
+ } else {
+ int p1 = is.readU8();
+ int p2 = is.readU8();
+ int p3 = is.readU8();
+ pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
+ }
+ return pix;
+ }
+
+ private void readPixels(InStream is, int[] dst, int count) throws Exception {
+ if (bytesPerPixel == 1) {
+ byte[] buf = new byte[count];
+ is.readBytes(buf, 0, count);
+ for (int i = 0; i < count; i++) {
+ dst[i] = (int)buf[i] & 0xFF;
+ }
+ } else {
+ byte[] buf = new byte[count * 3];
+ is.readBytes(buf, 0, count * 3);
+ for (int i = 0; i < count; i++) {
+ dst[i] = ((buf[i*3+2] & 0xFF) << 16 |
+ (buf[i*3+1] & 0xFF) << 8 |
+ (buf[i*3] & 0xFF));
+ }
+ }
+ }
+
+ private void readZrlePalette(int[] palette, int palSize) throws Exception {
+ readPixels(zrleInStream, palette, palSize);
+ }
+
+ private void readZrleRawPixels(int tw, int th) throws Exception {
+ if (bytesPerPixel == 1) {
+ zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
+ } else {
+ readPixels(zrleInStream, zrleTilePixels24, tw * th); ///
+ }
+ }
+
+ private void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
+ throws Exception {
+
+ int bppp = ((palSize > 16) ? 8 :
+ ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+ int ptr = 0;
+
+ for (int i = 0; i < th; i++) {
+ int eol = ptr + tw;
+ int b = 0;
+ int nbits = 0;
+
+ while (ptr < eol) {
+ if (nbits == 0) {
+ b = zrleInStream.readU8();
+ nbits = 8;
+ }
+ nbits -= bppp;
+ int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
+ if (bytesPerPixel == 1) {
+ zrleTilePixels8[ptr++] = (byte)palette[index];
+ } else {
+ zrleTilePixels24[ptr++] = palette[index];
+ }
+ }
+ }
+ }
+
+ private void readZrlePlainRLEPixels(int tw, int th) throws Exception {
+ int ptr = 0;
+ int end = ptr + tw * th;
+ while (ptr < end) {
+ int pix = readPixel(zrleInStream);
+ int len = 1;
+ int b;
+ do {
+ b = zrleInStream.readU8();
+ len += b;
+ } while (b == 255);
+
+ if (!(len <= end - ptr))
+ throw new Exception("ZRLE decoder: assertion failed" +
+ " (len <= end-ptr)");
+
+ if (bytesPerPixel == 1) {
+ while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix;
+ } else {
+ while (len-- > 0) zrleTilePixels24[ptr++] = pix;
+ }
+ }
+ }
+
+ private void readZrlePackedRLEPixels(int tw, int th, int[] palette)
+ throws Exception {
+
+ int ptr = 0;
+ int end = ptr + tw * th;
+ while (ptr < end) {
+ int index = zrleInStream.readU8();
+ int len = 1;
+ if ((index & 128) != 0) {
+ int b;
+ do {
+ b = zrleInStream.readU8();
+ len += b;
+ } while (b == 255);
+
+ if (!(len <= end - ptr))
+ throw new Exception("ZRLE decoder: assertion failed" +
+ " (len <= end - ptr)");
+ }
+
+ index &= 127;
+ int pix = palette[index];
+
+ if (bytesPerPixel == 1) {
+ while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix;
+ } else {
+ while (len-- > 0) zrleTilePixels24[ptr++] = pix;
+ }
+ }
+ }
+
+ //
+ // ZRLE encoder's data.
+ //
+
+ private byte[] zrleBuf;
+ private int zrleBufLen = 0;
+ private byte[] zrleTilePixels8;
+ private int[] zrleTilePixels24;
+ private ZlibInStream zrleInStream;
+ private boolean zrleRecWarningShown = false;
+}
diff --git a/java/src/com/tigervnc/decoder/ZlibDecoder.java b/java/src/com/tigervnc/decoder/ZlibDecoder.java
new file mode 100644
index 00000000..1370da15
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/ZlibDecoder.java
@@ -0,0 +1,103 @@
+package com.tightvnc.decoder;
+
+import com.tightvnc.vncviewer.RfbInputStream;
+import java.awt.Graphics;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+//
+// Class that used for decoding ZLib encoded data.
+//
+
+public class ZlibDecoder extends RawDecoder {
+
+ final static int EncodingZlib = 6;
+
+ public ZlibDecoder(Graphics g, RfbInputStream is) {
+ super(g, is);
+ }
+
+ public ZlibDecoder(Graphics g, RfbInputStream is, int frameBufferW,
+ int frameBufferH) {
+ super(g, is, frameBufferW, frameBufferH);
+ }
+
+ //
+ // Override handleRect method to decode ZLib encoded data insted of
+ // raw pixel data.
+ //
+
+ public void handleRect(int x, int y, int w, int h) throws IOException {
+
+ //
+ // Write encoding ID to record output stream.
+ // Remark: we forced changed encoding from zlib to raw
+ // cause at this moment we cannot save data in zlib encoding.
+ //
+
+ if (dos != null) {
+ dos.writeInt(RawDecoder.EncodingRaw);
+ }
+
+ int nBytes = rfbis.readU32();
+
+ if (zlibBuf == null || zlibBufLen < nBytes) {
+ zlibBufLen = nBytes * 2;
+ zlibBuf = new byte[zlibBufLen];
+ }
+
+ rfbis.readFully(zlibBuf, 0, nBytes);
+
+ if (zlibInflater == null) {
+ zlibInflater = new Inflater();
+ }
+ zlibInflater.setInput(zlibBuf, 0, nBytes);
+
+ try {
+ if (bytesPerPixel == 1) {
+ for (int dy = y; dy < y + h; dy++) {
+ zlibInflater.inflate(pixels8, dy * framebufferWidth + x, w);
+
+ //
+ // Save decoded raw data to data output stream
+ //
+
+ if (dos != null)
+ dos.write(pixels8, dy * framebufferWidth + x, w);
+ }
+ } else {
+ byte[] buf = new byte[w * 4];
+ int i, offset;
+ for (int dy = y; dy < y + h; dy++) {
+ zlibInflater.inflate(buf);
+ offset = dy * framebufferWidth + x;
+ for (i = 0; i < w; i++) {
+ RawDecoder.pixels24[offset + i] =
+ (buf[i * 4 + 2] & 0xFF) << 16 |
+ (buf[i * 4 + 1] & 0xFF) << 8 |
+ (buf[i * 4] & 0xFF);
+ }
+
+ //
+ // Save decoded raw data to data output stream
+ //
+
+ if (dos != null)
+ dos.write(buf);
+ }
+ }
+ } catch (DataFormatException ex) {
+ ex.printStackTrace();
+ }
+ handleUpdatedPixels(x, y, w, h);
+ }
+
+ //
+ // Zlib encoder's data.
+ //
+
+ protected byte[] zlibBuf;
+ protected int zlibBufLen = 0;
+ protected Inflater zlibInflater;
+}
diff --git a/java/src/com/tigervnc/decoder/common/Repaintable.java b/java/src/com/tigervnc/decoder/common/Repaintable.java
new file mode 100644
index 00000000..de11f4c8
--- /dev/null
+++ b/java/src/com/tigervnc/decoder/common/Repaintable.java
@@ -0,0 +1,7 @@
+package com.tightvnc.decoder.common;
+
+public interface Repaintable {
+
+ public void scheduleRepaint(int x, int y, int w, int h);
+
+}