]> source.dussan.org Git - tigervnc.git/commitdiff
[Developement] Added decode methods to Tight decoder class.
authorenikey <enikey@3789f03b-4d11-0410-bbf8-ca57d06f2519>
Fri, 19 Dec 2008 08:16:45 +0000 (08:16 +0000)
committerenikey <enikey@3789f03b-4d11-0410-bbf8-ca57d06f2519>
Fri, 19 Dec 2008 08:16:45 +0000 (08:16 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3433 3789f03b-4d11-0410-bbf8-ca57d06f2519

java/src/com/tightvnc/decoder/TightDecoder.java

index 476dac679051c0d62826ed6bed24ce839edbf3cb..e903820560ad5148bcdcaa660655826e41e7856c 100644 (file)
@@ -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
@@ -63,6 +63,281 @@ public class TightDecoder extends RawDecoder {
     statNumRectsTightJPEG = v;
   }
 
+  //
+  // 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).
   //
@@ -153,6 +428,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
   //