changeset 539:0d11471e0b5b

add comment ZRLEDecoder
author e165729 <e165729@ie.u-ryukyu.ac.jp>
date Thu, 05 Sep 2019 19:05:02 +0900
parents 6620e04f994c
children b86d445685c7
files .idea/vcs.xml src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java
diffstat 2 files changed, 135 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/.idea/vcs.xml	Thu Jun 13 18:19:53 2019 +0900
+++ b/.idea/vcs.xml	Thu Sep 05 19:05:02 2019 +0900
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="" vcs="hg4idea" />
+    <mapping directory="$PROJECT_DIR$" vcs="hg4idea" />
   </component>
 </project>
\ No newline at end of file
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java	Thu Jun 13 18:19:53 2019 +0900
+++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java	Thu Sep 05 19:05:02 2019 +0900
@@ -38,21 +38,21 @@
 
 public class ZRLEDecoder extends ZlibDecoder {
 	private static final int MAX_TILE_SIZE = 64;
-    private int[] decodedBitmap;
-    private int[] palette;
+	private int[] decodedBitmap;
+	private int[] palette;
 
-    class TileLoop {
+	class TileLoop {
 		private final boolean blocking;
 		private final int half;
-		private int deflate_size = 55507;
-		private ByteBuffer c1;
+		private int deflate_size = 55507;  // 圧縮サイズ
+		private ByteBuffer c1;            // パケット?
 		private int width; // phase2 length
 		private FramebufferUpdateRectangle c1rect;
 		private int c1headerPos;
 		private int prevLineOffset;
 		private int prevC1Offset;
 		private int prevoffset;
-		private Deflater deflater;
+		private Deflater deflater;     // 圧縮クラス
 		private int rectPos;
 		private int ztileInLine;
 		private int hwidth;
@@ -66,6 +66,9 @@
 		 * read FrameBuffferUpdate. If it is ZLE, make it ZLEE which is self contained compressed packet.
 		 * put the packet to the multicastqueue. Then normal rendering engine read the same stream using is.reset().
 		 *
+		 * framebufferUpdateを子nodeに送信する際に、ZLEを辞書をつけてZLEEのパケットに変換する
+		 * multicast queueに入れる。普通のレンダリングではis.reset()を使って同じストリームを読み込む
+		 *
 		 * Haeder
 		 *    messageID  ( FrameBuffer Update
 		 *    1 byte padding
@@ -82,16 +85,20 @@
 		 * @throws UnsupportedEncodingException
 		 */
 
+		// コンストラクタ
 		public TileLoop(int offset) {
-			prevoffset = prevLineOffset = prevC1Offset = offset;
+			prevoffset = prevLineOffset = prevC1Offset = offset;   // オフセット記憶群に代入
 			if (offset < deflate_size+spanGap) {
 				// packet size fit in broadcast send it all at once
+				// ブロードキャストに適合するパケットサイズで一斉に送信
+				// 十分に小さいのでブロッキングせずに送信
 				blocking = false;
 			} else
 				blocking = true;
 			discard = 0; half = 0;
 		}
 
+		// ?: ブロッキングしたものをc1に渡している?
 		private void zrleeBlocking(TreeRFBProto rfb, ByteBuffer header, FramebufferUpdateRectangle rect, byte bytes[]) {
 			// dump32(inputs);
 			deflater = rfb.deflater;
@@ -99,17 +106,18 @@
 			c1.put(header.get(0));
 			c1rect = new FramebufferUpdateRectangle(rect.x, rect.y, 0, 0);
 			if (!blocking) {
-				deflater.setInput(bytes,0,prevoffset);
-				deflater.deflate(c1);
+				deflater.setInput(bytes,0,prevoffset);    // bytesを圧縮データとして0からprevoffsetまでをdeflater内部にセットする
+				deflater.deflate(c1);						// setInputで入力したデータを圧縮してc1に入れる
 				flushMuticast(rfb);
 			}
 			return;
 		}
 
+		// ?: c1の値を設定している実部分? c1(=パケット)にheaderを設定してるっぽい? 要: TreeRFBProto読み
 		private void newMulticastPacket(TreeRFBProto rfb, FramebufferUpdateRectangle rect) {
-			c1 = rfb.multicastqueue.allocate(deflate_size);
+			c1 = rfb.multicastqueue.allocate(deflate_size);  // ByteBufferのallocateと同等
 			if (rfb.addSerialNum)
-				c1.putLong(rfb.counter++);
+				c1.putLong(rfb.counter++);   // 現在のByteBufferにlongの値を書き込み
 			if (rfb.checkDelay)
 				CheckDelay.checkDelay(c1, rect.x, rect.y, rect.width, rect.height, System.currentTimeMillis(), EncodingType.CHECK_DELAY);
 			c1headerPos = c1.position();
@@ -123,12 +131,16 @@
 			ztileInLine = 0;
 		}
 
+		// oh... フィールド変数です...
 		int spanGap = 128;
 		/**
 		 *
 		 * Series of tiles compose at most three rectangles. SYNC_FLUSH is necessary on
 		 * rectangle boundaries.
 		 *
+		 * tileは最大3つの長方形に分けられる
+		 * をSYNC_FLUSH(パケットへ書き込み)する際は長方形の区切りの部分(境界部分)である必要がある
+		 *
 		 *                +----+
 		 *                |  | |   phase 0
 		 *     +---------------+
@@ -150,6 +162,21 @@
 		 *     may failed again and restart the from this point.
 		 *     The next packet start with later 256 tiles filled and unflushed.
 		 *
+		 * ブロードキャストパケットは64KB以内である必要がある
+		 * タイルは64(縦)x64(横)x3(RGB)= 11288Byteであり、圧縮しない場合だと5tile分をパケットに含めることができる
+		 * tileを圧縮すると10~100タイルを含めることができる。しかし、どのくらい圧縮できるかは予想不可能
+		 * 圧縮容量はDeflate.needsInputs()で確認することができる
+		 * SYNC_FLUSHした時にneedsInputs()がfalseだった場合、書き込みを少なくする必要がある→パケットの容量不足、書き込み量を減らす
+		 *
+		 * あるphaseでSYNC_FLUSHをする前に512tileの書き込みを試す
+		 * 失敗した場合半分の256tileで試す
+		 * 更に失敗した場合は前の行までで書き込みを行い、新しいパケットで512tileを試す
+		 * 更に更に失敗した場合は前半分の256タイルで書き込みを試す
+		 * それでも失敗した場合は前半分の256タイルを破棄する??
+		 * 最後の場合は起こりえないが、次の256タイル(後半部256タイル)が失敗して一つ前の段階(前半部256タイル)から再開する可能性があるため
+		 * 前半部を書き込む必要がある
+		 * 次のパケットは、後の256タイルがいっぱいになってフラッシュされた状態で始まります。(次のパケットは後半部の256タイルが書き込まれていない状態で始まる?)
+		 *
 		 * @param rfb
 		 * @param last
 		 * @param rect
@@ -158,12 +185,13 @@
 		 * @param tileW
 		 * @param tileH
 		 */
-		
-		int MAX_ZTILE = 512;
 
+		int MAX_ZTILE = 512;   // 一つのパケットで圧縮を試す、最大のタイル数
+
+		// フェーズ分けして、ブロッキングを行なっている部分
 		public void multicastPut(TreeRFBProto rfb, boolean last, FramebufferUpdateRectangle rect, byte[] bytes, int offset, int tileW, int tileH) {
 			if (!blocking) return;
-			int span = offset - prevoffset;
+			int span = offset - prevoffset;    // ?: 現在の位置調整?
 			deflater.setInput(bytes,prevoffset,span);
 			prevoffset = offset;
 			c1rect.width  += tileW; width += tileW;
@@ -175,7 +203,7 @@
 					c1rect.height += tileH;
 					compressAndCheckFlush(rfb,rect,bytes,offset,true, last);
 				}
-			} else {  // phase 1
+			} else if (!last && c1.remaining() > spanGap) { // phase 1
 				if (width >= rect.width) {
 					c1rect.width = rect.width;
 					width = 0;
@@ -184,40 +212,87 @@
 					prevC1Offset = c1.position();
 					compressAndCheckFlush(rfb,rect,bytes,offset,true, last);
 				} else {
-					compressAndCheckFlush(rfb, rect, bytes, offset, false, last);
+					compressAndCheckFlush(rfb,rect,bytes,offset,false, last);
+				}
+			} else { // phase2
+				//  rewind to the last line finish phase 1
+				int savew = width;
+				c1.position(prevC1Offset);
+				flushRectangle(rect);
+				if (savew>0) {
+					// recompress overrun and flush phase 2
+					c1rect.width = savew;
+					c1rect.height = tileH;
+					compressAndCheckFlush(rfb,rect,bytes,offset,true, last);
 				}
 			}
 		}
 
 		private void compressAndCheckFlush(TreeRFBProto rfb, FramebufferUpdateRectangle rect, byte[] bytes, int offset, boolean flush, boolean last) {
 			ztileInLine++;
-
-			deflater.deflate(c1, Deflater.NO_FLUSH);
-			int headerLength = 20;
-			if (!deflater.needsInput()) {
-				deflater.finish();
-				if (offset != prevLineOffset) {  // phase 2
-					c1.limit(c1.limit() + headerLength);
-					// to make rectangle header shift last bytes
-					for (int i = 0; i < c1.position() - prevC1Offset; i++) {
-						c1.array()[prevC1Offset + headerLength - i] = c1.array() [prevC1Offset - i];
+			if (!flush && ztileInLine < MAX_ZTILE) {
+				if (ztileInLine == MAX_ZTILE/2) {
+					hwidth = width; hc1width = c1rect.width; hoffset = offset; hc1offset = c1.position();
+				}
+				deflater.deflate(c1, Deflater.NO_FLUSH);
+			} else {
+				deflater.deflate(c1, Deflater.SYNC_FLUSH);
+				if (!deflater.needsInput()) {
+					// too large, try half line
+					width = hwidth;
+					c1rect.width = hc1width;
+					c1.position(hc1offset);
+					deflater.setInput(bytes, prevLineOffset, hoffset - prevC1Offset);
+					deflater.deflate(c1, Deflater.SYNC_FLUSH);
+					int from, len;
+					if (!deflater.needsInput()) {
+						// flush previous line and start new packet
+						c1.position(prevC1Offset);
+						unputrectangle();
+						flushMuticast(rfb);
+						newMulticastPacket(rfb, rect);
+						nextRectangle(rect);
+						// we already reached MIX_ZTILE do half of them, do compress right now
+						from = prevC1Offset;
+						len = offset - prevLineOffset;
+						deflater.setInput(bytes, from, len);
+						deflater.deflate(c1, Deflater.SYNC_FLUSH);
+						if (deflater.needsInput()) {
+							flushRectangle(rect);  // we are the flushed last line
+							return;
+						}
+						// half size should always succeed
+						from = prevC1Offset;
+						len = hoffset - prevLineOffset;
+						deflater.setInput(bytes, from, len);
+						deflater.deflate(c1, Deflater.SYNC_FLUSH);
+						if (!deflater.needsInput()) { /* fatal error discard this line */
+							discard++;
+							if (!last) {
+								newMulticastPacket(rfb, rect);
+								nextRectangle(rect);
+							}
+							return;
+						} else
+							flushRectangle(rect);  // normal case
+						// later half is remain continue
+					} else {
+						flushMuticast(rfb);
+						newMulticastPacket(rfb,rect);
+						nextRectangle(rect);
 					}
-					c1.putShort(prevC1Offset + 1,  (short)c1rect.x);
-					c1.putShort(rectPos + 3,  (short)c1rect.y);
-					c1.putShort(rectPos + 5,  (short)c1rect.width);
-					c1.putShort(rectPos + 7, (short)c1rect.height);
-					c1.putInt(rectPos + 9,EncodingType.ZRLEE.getId());
-					c1.putInt(rectPos + 13, c1.position()-rectPos-12); // data length
-					c1.putShort(2,(short)(c1.getShort(2)+1));    // increment rectangle count
+					// do later half in new Packet
+					from = hoffset ; len = offset - hoffset;
+					deflater.setInput(bytes, from, len);
+					deflater.deflate(c1, Deflater.NO_FLUSH);
+					hwidth = width;
+					hoffset = offset;
+					hc1offset = c1.position();
+					ztileInLine = MAX_ZTILE/2;
 				}
-				flushMuticast(rfb);
-				newMulticastPacket(rfb, rect);
-				deflater.deflate(c1, Deflater.NO_FLUSH);
 			}
-			nextRectangle(rect);
 		}
 
-
 		/**
 		 * fix rectangle header
 		 * create next rectangle header
@@ -237,6 +312,10 @@
 			ztileInLine = 0;
 		}
 
+		private void unputrectangle() {
+			c1.putShort(2,(short)(c1.getShort(2)-1));    // last rectangle is canceled
+		}
+
 		private void nextRectangle(FramebufferUpdateRectangle rect) {
 			if (c1rect.x+c1rect.width < rect.x+rect.width) {
 				c1rect.x = c1rect.width;   // next rectangle is phase 1
@@ -271,19 +350,19 @@
 
 	@Override
 	public void decode(Reader reader, Renderer renderer,
-                       FramebufferUpdateRectangle rect) throws TransportException {
+					   FramebufferUpdateRectangle rect) throws TransportException {
 		int zippedLength = (int) reader.readUInt32();
 		if (0 == zippedLength) return;
 		int length = rect.width * rect.height * renderer.getBytesPerPixel();
 		byte[] bytes = unzip(reader, zippedLength, length, rect.getEncodingType());
 		decode1(renderer, null, rect, bytes, zippedLength, null);
-    }
+	}
 
 
 	public void multicastDecode(Reader reader, Renderer renderer,
-					   FramebufferUpdateRectangle rect, TreeRFBProto rfb) throws TransportException {
-    	ByteBuffer header = ByteBuffer.allocate(16);
-    	reader.read(header.array());
+								FramebufferUpdateRectangle rect, TreeRFBProto rfb) throws TransportException {
+		ByteBuffer header = ByteBuffer.allocate(16);
+		reader.read(header.array());
 		int zippedLength = (int) reader.readUInt32();
 		if (0 == zippedLength) return;
 		int length = rect.width * rect.height * renderer.getBytesPerPixel();
@@ -298,12 +377,12 @@
 
 		TileLoop tileloop = new TileLoop(zippedLength);
 		//System.out.println("decode1: "+rect);
-        if (null == palette) {
-            palette = new int [128];
-        }
-        if (null == decodedBitmap) {
-            decodedBitmap = new int[MAX_TILE_SIZE * MAX_TILE_SIZE];
-        }
+		if (null == palette) {
+			palette = new int [128];
+		}
+		if (null == decodedBitmap) {
+			decodedBitmap = new int[MAX_TILE_SIZE * MAX_TILE_SIZE];
+		}
 
 		if (rfbProto.multicastBlocking)  {
 			tileloop.zrleeBlocking(rfbProto, header, rect,bytes);
@@ -344,7 +423,7 @@
 	}
 
 	private int decodePlainRle(byte[] bytes, int offset, Renderer renderer,
-			int tileX, int tileY, int tileWidth, int tileHeight) {
+							   int tileX, int tileY, int tileWidth, int tileHeight) {
 		int bytesPerCPixel = renderer.getBytesPerCPixel();
 		int decodedOffset = 0;
 		int decodedEnd = tileWidth * tileHeight;
@@ -365,7 +444,7 @@
 	}
 
 	private int decodePaletteRle(byte[] bytes, int offset, Renderer renderer,
-                                 int tileX, int tileY, int tileWidth, int tileHeight) {
+								 int tileX, int tileY, int tileWidth, int tileHeight) {
 		int decodedOffset = 0;
 		int decodedEnd = tileWidth * tileHeight;
 		int index = offset;
@@ -387,7 +466,7 @@
 	}
 
 	private int decodePacked(byte[] bytes, int offset, Renderer renderer,
-                             int paletteSize, int tileX, int tileY, int tileWidth, int tileHeight) {
+							 int paletteSize, int tileX, int tileY, int tileWidth, int tileHeight) {
 		int bitsPerPalletedPixel = paletteSize > 16 ? 8 : paletteSize > 4 ? 4
 				: paletteSize > 2 ? 2 : 1;
 		int packedOffset = offset;
@@ -414,14 +493,14 @@
 	}
 
 	private int decodeRaw(byte[] bytes, int offset, Renderer renderer,
-			int tileX, int tileY, int tileWidth, int tileHeight) throws TransportException {
+						  int tileX, int tileY, int tileWidth, int tileHeight) throws TransportException {
 		return renderer.drawCompactBytes(bytes, offset, tileX, tileY, tileWidth, tileHeight);
 	}
 
 	private int readPalette(byte[] bytes, int offset, Renderer renderer, int paletteSize) {
-        final int bytesPerCPixel = renderer.getBytesPerCPixel();
+		final int bytesPerCPixel = renderer.getBytesPerCPixel();
 		for (int i=0; i<paletteSize; ++i) {
-            palette[i] = renderer.getCompactPixelColor(bytes, offset + i* bytesPerCPixel);
+			palette[i] = renderer.getCompactPixelColor(bytes, offset + i* bytesPerCPixel);
 		}
 		return paletteSize * bytesPerCPixel;
 	}