diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/src/com/tightvnc/decoder/TightDecoder.java | 302 |
1 files changed, 301 insertions, 1 deletions
diff --git a/java/src/com/tightvnc/decoder/TightDecoder.java b/java/src/com/tightvnc/decoder/TightDecoder.java index 476dac67..e9038205 100644 --- a/java/src/com/tightvnc/decoder/TightDecoder.java +++ b/java/src/com/tightvnc/decoder/TightDecoder.java @@ -14,7 +14,7 @@ import java.util.zip.Inflater; // Class that used for decoding Tight encoded data. // -public class TightDecoder extends RawDecoder { +public class TightDecoder extends RawDecoder implements ImageObserver { // // Tight decoder constants @@ -64,6 +64,281 @@ public class TightDecoder extends RawDecoder { } // + // Handle a Tight-encoded rectangle. + // + + public void handleRect(int x, int y, int w, int h) throws Exception { + + int comp_ctl = rfbis.readU8(); + if (rec.canWrite()) { + if (rec.isRecordFromBeginning() || + comp_ctl == (TightFill << 4) || + comp_ctl == (TightJpeg << 4)) { + // Send data exactly as received. + rec.writeByte(comp_ctl); + } else { + // Tell the decoder to flush each of the four zlib streams. + rec.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 (rec.canWrite()) { + rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfbis.readFully(buf); + if (rec.canWrite()) { + rec.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 (rec.canWrite()) { + if (!rec.isRecordFromBeginning()) { + rec.recordCompactLen(jpegData.length); + } + rec.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; + + } + + // 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 (rec.canWrite()) { + rec.writeByte(filter_id); + } + if (filter_id == TightDecoder.TightFilterPalette) { + numColors = rfbis.readU8() + 1; + if (rec.canWrite()) { + rec.writeByte((numColors - 1)); + } + if (bytesPerPixel == 1) { + if (numColors != 2) { + throw new Exception("Incorrect tight palette size: " + numColors); + } + rfbis.readFully(palette8); + if (rec.canWrite()) { + rec.write(palette8); + } + } else { + byte[] buf = new byte[numColors * 3]; + rfbis.readFully(buf); + if (rec.canWrite()) { + rec.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 (rec.canWrite()) { + rec.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 (rec.canWrite()) { + rec.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 (rec.canWrite()) { + rec.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 (rec.canWrite()) { + rec.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); + if ( (rec.canWrite()) && (rec.isRecordFromBeginning()) ) { + rec.write(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 ( (rec.canWrite()) && (!rec.isRecordFromBeginning()) ) { + rec.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). // @@ -154,6 +429,31 @@ public class TightDecoder extends RawDecoder { } // + // 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. + } + } + + // // Private members // |