comparison src/ProxyVncCanvas.java @ 15:89e1c5f84407

faile ZRLE encode. success raw encode.
author e085711
date Sun, 17 Apr 2011 19:33:01 +0900
parents 57f227139599
children 4eda2dbb7fef
comparison
equal deleted inserted replaced
14:57f227139599 15:89e1c5f84407
1
2
3 import java.awt.*; 1 import java.awt.*;
4 import java.awt.event.*; 2 import java.awt.event.*;
5 import java.awt.image.*; 3 import java.awt.image.*;
6 import java.io.*; 4 import java.io.*;
7 import java.lang.*; 5 import java.lang.*;
13 // 11 //
14 //VncCanvas is a subclass of Canvas which draws a VNC desktop on it. 12 //VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
15 // 13 //
16 14
17 class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener, 15 class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener,
18 MouseMotionListener { 16 MouseMotionListener {
19 17
20 VncProxyService viewer; 18 VncProxyService viewer;
21 MyRfbProto rfb; 19 MyRfbProto rfb;
22 ColorModel cm8, cm24; 20 ColorModel cm8, cm24;
23 Color[] colors; 21 Color[] colors;
24 int bytesPixel; 22 int bytesPixel;
25 23
26 int maxWidth = 0, maxHeight = 0; 24 int maxWidth = 0, maxHeight = 0;
27 int scalingFactor; 25 int scalingFactor;
28 int scaledWidth, scaledHeight; 26 int scaledWidth, scaledHeight;
29 27
30 Image memImage; 28 Image memImage;
31 Graphics memGraphics; 29 Graphics memGraphics;
32 30
33 Image rawPixelsImage; 31 Image rawPixelsImage;
34 MemoryImageSource pixelsSource; 32 MemoryImageSource pixelsSource;
35 byte[] pixels8; 33 byte[] pixels8;
36 int[] pixels24; 34 int[] pixels24;
37 35
38 // Update statistics. 36 // Update statistics.
39 long statStartTime; // time on first framebufferUpdateRequest 37 long statStartTime; // time on first framebufferUpdateRequest
40 int statNumUpdates; // counter for FramebufferUpdate messages 38 int statNumUpdates; // counter for FramebufferUpdate messages
41 int statNumTotalRects; // rectangles in FramebufferUpdate messages 39 int statNumTotalRects; // rectangles in FramebufferUpdate messages
42 int statNumPixelRects; // the same, but excluding pseudo-rectangles 40 int statNumPixelRects; // the same, but excluding pseudo-rectangles
43 int statNumRectsTight; // Tight-encoded rectangles (including JPEG) 41 int statNumRectsTight; // Tight-encoded rectangles (including JPEG)
44 int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles 42 int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
45 int statNumRectsZRLE; // ZRLE-encoded rectangles 43 int statNumRectsZRLE; // ZRLE-encoded rectangles
46 int statNumRectsHextile; // Hextile-encoded rectangles 44 int statNumRectsHextile; // Hextile-encoded rectangles
47 int statNumRectsRaw; // Raw-encoded rectangles 45 int statNumRectsRaw; // Raw-encoded rectangles
48 int statNumRectsCopy; // CopyRect rectangles 46 int statNumRectsCopy; // CopyRect rectangles
49 int statNumBytesEncoded; // number of bytes in updates, as received 47 int statNumBytesEncoded; // number of bytes in updates, as received
50 int statNumBytesDecoded; // number of bytes, as if Raw encoding was used 48 int statNumBytesDecoded; // number of bytes, as if Raw encoding was used
51 49
52 // ZRLE encoder's data. 50 // ZRLE encoder's data.
53 byte[] zrleBuf; 51 byte[] zrleBuf;
54 int zrleBufLen = 0; 52 int zrleBufLen = 0;
55 byte[] zrleTilePixels8; 53 byte[] zrleTilePixels8;
56 int[] zrleTilePixels24; 54 int[] zrleTilePixels24;
57 ZlibInStream zrleInStream; 55 ZlibInStream zrleInStream;
58 boolean zrleRecWarningShown = false; 56 boolean zrleRecWarningShown = false;
59 57
60 // Zlib encoder's data. 58 // Zlib encoder's data.
61 byte[] zlibBuf; 59 byte[] zlibBuf;
62 int zlibBufLen = 0; 60 int zlibBufLen = 0;
63 Inflater zlibInflater; 61 Inflater zlibInflater;
64 62
65 // Tight encoder's data. 63 // Tight encoder's data.
66 final static int tightZlibBufferSize = 512; 64 final static int tightZlibBufferSize = 512;
67 Inflater[] tightInflaters; 65 Inflater[] tightInflaters;
68 66
69 // Since JPEG images are loaded asynchronously, we have to remember 67 // Since JPEG images are loaded asynchronously, we have to remember
70 // their position in the framebuffer. Also, this jpegRect object is 68 // their position in the framebuffer. Also, this jpegRect object is
71 // used for synchronization between the rfbThread and a JVM's thread 69 // used for synchronization between the rfbThread and a JVM's thread
72 // which decodes and loads JPEG images. 70 // which decodes and loads JPEG images.
73 Rectangle jpegRect; 71 Rectangle jpegRect;
74 72
75 // True if we process keyboard and mouse events. 73 // True if we process keyboard and mouse events.
76 boolean inputEnabled; 74 boolean inputEnabled;
77 75
78 // 76
79 // The constructors. 77 //
80 // 78 // The constructors.
81 79 //
82 public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_) 80
83 throws IOException { 81 public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_)
84 82 throws IOException {
85 viewer = v; 83
86 maxWidth = maxWidth_; 84 viewer = v;
87 maxHeight = maxHeight_; 85 maxWidth = maxWidth_;
88 86 maxHeight = maxHeight_;
89 rfb = viewer.rfb; 87
90 88 rfb = viewer.rfb;
91 tightInflaters = new Inflater[4]; 89
92 90 tightInflaters = new Inflater[4];
93 cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); 91
94 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); 92 cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
95 93 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
96 colors = new Color[256]; 94
97 for (int i = 0; i < 256; i++) 95 colors = new Color[256];
98 colors[i] = new Color(cm8.getRGB(i)); 96 for (int i = 0; i < 256; i++)
99 97 colors[i] = new Color(cm8.getRGB(i));
100 // setPixelFormat(); 98
101 99 // setPixelFormat();
102 inputEnabled = false; 100
103 // Keyboard listener is enabled even in view-only mode, to catch 101 inputEnabled = false;
104 // 'r' or 'R' key presses used to request screen update. 102 // Keyboard listener is enabled even in view-only mode, to catch
105 addKeyListener(this); 103 // 'r' or 'R' key presses used to request screen update.
106 } 104 addKeyListener(this);
107 105 }
108 public ProxyVncCanvas(VncProxyService v) throws IOException { 106
109 this(v, 0, 0); 107 public ProxyVncCanvas(VncProxyService v) throws IOException {
110 } 108 this(v, 0, 0);
111 109 }
112 // 110
113 // Callback methods to determine geometry of our Component. 111 //
114 // 112 // Callback methods to determine geometry of our Component.
115 113 //
116 public Dimension getPreferredSize() { 114
117 return new Dimension(scaledWidth, scaledHeight); 115 public Dimension getPreferredSize() {
118 } 116 return new Dimension(scaledWidth, scaledHeight);
119 117 }
120 public Dimension getMinimumSize() { 118
121 return new Dimension(scaledWidth, scaledHeight); 119 public Dimension getMinimumSize() {
122 } 120 return new Dimension(scaledWidth, scaledHeight);
123 121 }
124 public Dimension getMaximumSize() { 122
125 return new Dimension(scaledWidth, scaledHeight); 123 public Dimension getMaximumSize() {
126 } 124 return new Dimension(scaledWidth, scaledHeight);
127 125 }
128 // 126
129 // All painting is performed here. 127 //
130 // 128 // All painting is performed here.
131 129 //
132 public void update(Graphics g) { 130
133 paint(g); 131 public void update(Graphics g) {
134 } 132 paint(g);
135 133 }
136 public void paint(Graphics g) { 134
137 synchronized (memImage) { 135 public void paint(Graphics g) {
138 if (rfb.framebufferWidth == scaledWidth) { 136 synchronized (memImage) {
139 g.drawImage(memImage, 0, 0, null); 137 if (rfb.framebufferWidth == scaledWidth) {
138 g.drawImage(memImage, 0, 0, null);
139 } else {
140 paintScaledFrameBuffer(g);
141 }
142 }
143 if (showSoftCursor) {
144 int x0 = cursorX - hotX, y0 = cursorY - hotY;
145 Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight);
146 if (r.intersects(g.getClipBounds())) {
147 g.drawImage(softCursor, x0, y0, null);
148 }
149 }
150 }
151
152 public void paintScaledFrameBuffer(Graphics g) {
153 g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null);
154 }
155
156 //
157 // Override the ImageObserver interface method to handle drawing of
158 // JPEG-encoded data.
159 //
160
161 public boolean imageUpdate(Image img, int infoflags, int x, int y,
162 int width, int height) {
163 if ((infoflags & (ALLBITS | ABORT)) == 0) {
164 return true; // We need more image data.
140 } else { 165 } else {
141 paintScaledFrameBuffer(g); 166 // If the whole image is available, draw it now.
142 } 167 if ((infoflags & ALLBITS) != 0) {
143 } 168 if (jpegRect != null) {
144 if (showSoftCursor) { 169 synchronized (jpegRect) {
145 int x0 = cursorX - hotX, y0 = cursorY - hotY; 170 memGraphics
146 Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); 171 .drawImage(img, jpegRect.x, jpegRect.y, null);
147 if (r.intersects(g.getClipBounds())) { 172 scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width,
148 g.drawImage(softCursor, x0, y0, null); 173 jpegRect.height);
149 } 174 jpegRect.notify();
150 } 175 }
151 } 176 }
152 177 }
153 public void paintScaledFrameBuffer(Graphics g) { 178 return false; // All image data was processed.
154 g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); 179 }
155 } 180 }
156 181
157 // 182 //
158 // Override the ImageObserver interface method to handle drawing of 183 // Start/stop receiving mouse events. Keyboard events are received
159 // JPEG-encoded data. 184 // even in view-only mode, because we want to map the 'r' key to the
160 // 185 // screen refreshing function.
161 186 //
162 public boolean imageUpdate(Image img, int infoflags, int x, int y, 187
163 int width, int height) { 188 public synchronized void enableInput(boolean enable) {
164 if ((infoflags & (ALLBITS | ABORT)) == 0) { 189 if (enable && !inputEnabled) {
165 return true; // We need more image data. 190 inputEnabled = true;
166 } else { 191 addMouseListener(this);
167 // If the whole image is available, draw it now. 192 addMouseMotionListener(this);
168 if ((infoflags & ALLBITS) != 0) { 193 if (viewer.showControls) {
169 if (jpegRect != null) { 194 viewer.buttonPanel.enableRemoteAccessControls(true);
170 synchronized (jpegRect) { 195 }
171 memGraphics 196 createSoftCursor(); // scaled cursor
172 .drawImage(img, jpegRect.x, jpegRect.y, null); 197 } else if (!enable && inputEnabled) {
173 scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width, 198 inputEnabled = false;
174 jpegRect.height); 199 removeMouseListener(this);
175 jpegRect.notify(); 200 removeMouseMotionListener(this);
176 } 201 if (viewer.showControls) {
177 } 202 viewer.buttonPanel.enableRemoteAccessControls(false);
178 } 203 }
179 return false; // All image data was processed. 204 createSoftCursor(); // non-scaled cursor
180 } 205 }
181 } 206 }
182 207
183 // 208 public void setPixelFormat() throws IOException {
184 // Start/stop receiving mouse events. Keyboard events are received 209 if (viewer.options.eightBitColors) {
185 // even in view-only mode, because we want to map the 'r' key to the 210 rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6);
186 // screen refreshing function. 211 bytesPixel = 1;
187 // 212 } else {
188 213 rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8,
189 public synchronized void enableInput(boolean enable) { 214 0);
190 if (enable && !inputEnabled) { 215 bytesPixel = 4;
191 inputEnabled = true; 216 }
192 addMouseListener(this); 217 updateFramebufferSize();
193 addMouseMotionListener(this); 218 }
194 if (viewer.showControls) { 219
195 viewer.buttonPanel.enableRemoteAccessControls(true); 220 void updateFramebufferSize() {
196 } 221
197 createSoftCursor(); // scaled cursor 222 // Useful shortcuts.
198 } else if (!enable && inputEnabled) { 223 int fbWidth = rfb.framebufferWidth;
199 inputEnabled = false; 224 int fbHeight = rfb.framebufferHeight;
200 removeMouseListener(this); 225
201 removeMouseMotionListener(this); 226 // Calculate scaling factor for auto scaling.
202 if (viewer.showControls) { 227 if (maxWidth > 0 && maxHeight > 0) {
203 viewer.buttonPanel.enableRemoteAccessControls(false); 228 int f1 = maxWidth * 100 / fbWidth;
204 } 229 int f2 = maxHeight * 100 / fbHeight;
205 createSoftCursor(); // non-scaled cursor 230 scalingFactor = Math.min(f1, f2);
206 } 231 if (scalingFactor > 100)
207 } 232 scalingFactor = 100;
208 233 System.out.println("Scaling desktop at " + scalingFactor + "%");
209 public void setPixelFormat() throws IOException { 234 }
210 if (viewer.options.eightBitColors) { 235
211 rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); 236 // Update scaled framebuffer geometry.
212 bytesPixel = 1; 237 scaledWidth = (fbWidth * scalingFactor + 50) / 100;
213 } else { 238 scaledHeight = (fbHeight * scalingFactor + 50) / 100;
214 rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 239
215 0); 240 // Create new off-screen image either if it does not exist, or if
216 bytesPixel = 4; 241 // its geometry should be changed. It's not necessary to replace
217 } 242 // existing image if only pixel format should be changed.
218 updateFramebufferSize(); 243 if (memImage == null) {
219 }
220
221 void updateFramebufferSize() {
222
223 // Useful shortcuts.
224 int fbWidth = rfb.framebufferWidth;
225 int fbHeight = rfb.framebufferHeight;
226
227 // Calculate scaling factor for auto scaling.
228 if (maxWidth > 0 && maxHeight > 0) {
229 int f1 = maxWidth * 100 / fbWidth;
230 int f2 = maxHeight * 100 / fbHeight;
231 scalingFactor = Math.min(f1, f2);
232 if (scalingFactor > 100)
233 scalingFactor = 100;
234 System.out.println("Scaling desktop at " + scalingFactor + "%");
235 }
236
237 // Update scaled framebuffer geometry.
238 scaledWidth = (fbWidth * scalingFactor + 50) / 100;
239 scaledHeight = (fbHeight * scalingFactor + 50) / 100;
240
241 // Create new off-screen image either if it does not exist, or if
242 // its geometry should be changed. It's not necessary to replace
243 // existing image if only pixel format should be changed.
244 if (memImage == null) {
245 memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
246 memGraphics = memImage.getGraphics();
247 } else if (memImage.getWidth(null) != fbWidth
248 || memImage.getHeight(null) != fbHeight) {
249 synchronized (memImage) {
250 memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); 244 memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
251 memGraphics = memImage.getGraphics(); 245 memGraphics = memImage.getGraphics();
252 } 246 } else if (memImage.getWidth(null) != fbWidth
253 } 247 || memImage.getHeight(null) != fbHeight) {
254 248 synchronized (memImage) {
255 // Images with raw pixels should be re-allocated on every change 249 memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
256 // of geometry or pixel format. 250 memGraphics = memImage.getGraphics();
257 if (bytesPixel == 1) { 251 }
258 252 }
259 pixels24 = null; 253
260 pixels8 = new byte[fbWidth * fbHeight]; 254 // Images with raw pixels should be re-allocated on every change
261 255 // of geometry or pixel format.
262 pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8, 256 if (bytesPixel == 1) {
263 pixels8, 0, fbWidth); 257
264 258 pixels24 = null;
265 zrleTilePixels24 = null; 259 pixels8 = new byte[fbWidth * fbHeight];
266 zrleTilePixels8 = new byte[64 * 64]; 260
267 261 pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8,
268 } else { 262 pixels8, 0, fbWidth);
269 263
270 pixels8 = null; 264 zrleTilePixels24 = null;
271 pixels24 = new int[fbWidth * fbHeight]; 265 zrleTilePixels8 = new byte[64 * 64];
272 266
273 pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, 267 } else {
274 pixels24, 0, fbWidth); 268
275 269 pixels8 = null;
276 zrleTilePixels8 = null; 270 pixels24 = new int[fbWidth * fbHeight];
277 zrleTilePixels24 = new int[64 * 64]; 271
278 272 pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24,
279 } 273 pixels24, 0, fbWidth);
280 pixelsSource.setAnimated(true); 274
281 rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); 275 zrleTilePixels8 = null;
282 276 zrleTilePixels24 = new int[64 * 64];
283 } 277
284 278 }
285 void resizeDesktopFrame() { 279 pixelsSource.setAnimated(true);
286 setSize(scaledWidth, scaledHeight); 280 rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource);
287 281
288 // FIXME: Find a better way to determine correct size of a 282 }
289 // ScrollPane. -- const 283
290 Insets insets = viewer.desktopScrollPane.getInsets(); 284 void resizeDesktopFrame() {
291 viewer.desktopScrollPane.setSize( 285 setSize(scaledWidth, scaledHeight);
292 scaledWidth + 2 * Math.min(insets.left, insets.right), 286
293 scaledHeight + 2 * Math.min(insets.top, insets.bottom)); 287 // FIXME: Find a better way to determine correct size of a
294 288 // ScrollPane. -- const
295 viewer.vncFrame.pack(); 289 Insets insets = viewer.desktopScrollPane.getInsets();
296 290 viewer.desktopScrollPane.setSize(
297 // Try to limit the frame size to the screen size. 291 scaledWidth + 2 * Math.min(insets.left, insets.right),
298 292 scaledHeight + 2 * Math.min(insets.top, insets.bottom));
299 Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); 293
300 Dimension frameSize = viewer.vncFrame.getSize(); 294 viewer.vncFrame.pack();
301 Dimension newSize = frameSize; 295
302 296 // Try to limit the frame size to the screen size.
303 // Reduce Screen Size by 30 pixels in each direction; 297
304 // This is a (poor) attempt to account for 298 Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
305 // 1) Menu bar on Macintosh (should really also account for 299 Dimension frameSize = viewer.vncFrame.getSize();
306 // Dock on OSX). Usually 22px on top of screen. 300 Dimension newSize = frameSize;
307 // 2) Taxkbar on Windows (usually about 28 px on bottom) 301
308 // 3) Other obstructions. 302 // Reduce Screen Size by 30 pixels in each direction;
309 303 // This is a (poor) attempt to account for
310 screenSize.height -= 30; 304 // 1) Menu bar on Macintosh (should really also account for
311 screenSize.width -= 30; 305 // Dock on OSX). Usually 22px on top of screen.
312 306 // 2) Taxkbar on Windows (usually about 28 px on bottom)
313 boolean needToResizeFrame = false; 307 // 3) Other obstructions.
314 if (frameSize.height > screenSize.height) { 308
315 newSize.height = screenSize.height; 309 screenSize.height -= 30;
316 needToResizeFrame = true; 310 screenSize.width -= 30;
317 } 311
318 if (frameSize.width > screenSize.width) { 312 boolean needToResizeFrame = false;
319 newSize.width = screenSize.width; 313 if (frameSize.height > screenSize.height) {
320 needToResizeFrame = true; 314 newSize.height = screenSize.height;
321 } 315 needToResizeFrame = true;
322 if (needToResizeFrame) { 316 }
323 viewer.vncFrame.setSize(newSize); 317 if (frameSize.width > screenSize.width) {
324 } 318 newSize.width = screenSize.width;
325 319 needToResizeFrame = true;
326 viewer.desktopScrollPane.doLayout(); 320 }
327 } 321 if (needToResizeFrame) {
328 322 viewer.vncFrame.setSize(newSize);
329 // 323 }
330 // processNormalProtocol() - executed by the rfbThread to deal with the 324
331 // RFB socket. 325 viewer.desktopScrollPane.doLayout();
332 // 326 }
333 327
334 public void processNormalProtocol() throws Exception { 328 //
335 329 // processNormalProtocol() - executed by the rfbThread to deal with the
336 // Start/stop session recording if necessary. 330 // RFB socket.
337 viewer.checkRecordingStatus(); 331 //
338 332
339 rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, 333 public void processNormalProtocol() throws Exception {
340 rfb.framebufferHeight, false); 334
341 335 // Start/stop session recording if necessary.
342 resetStats(); 336 viewer.checkRecordingStatus();
343 boolean statsRestarted = false; 337
344 338 rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
345 // 339 rfb.framebufferHeight, false);
346 // main dispatch loop 340
347 // 341 resetStats();
348 342 boolean statsRestarted = false;
349 long count = 0; 343
350 rfb.initServSock(5550); 344 //
351 try { 345 // main dispatch loop
346 //
347
348 long count = 0;
349 rfb.initServSock(5550);
350
351 // single thread
352 /*
353 try {
352 // rfb.setSoTimeout(1000); 354 // rfb.setSoTimeout(1000);
353 Socket newCli = rfb.accept(); 355 Socket newCli = rfb.accept();
354 rfb.sendInitData(newCli); 356 rfb.sendInitData(newCli);
355 rfb.addSock(newCli); 357 rfb.addSock(newCli);
356 } catch (IOException e) { 358 } catch (IOException e) {
357 } 359 }
358 /* 360 */
359 * Thread accept = new Thread(new acceptThread(rfb)); accept.start(); 361 // multi thread
360 */ 362 Thread accept = new Thread(new acceptThread(rfb));
361 while (true) { 363 accept.start();
362 364
363 if (rfb.MYVNC) { 365
364 if (rfb.cliSize() > 0) { 366 while (true) {
365 System.out.println("\ncount=" + count); 367
366 368 System.out.println("\ncount=" + count);
367 int nBytes = 0; 369 count++;
368 rfb.mark(20); 370
369 int msgType = rfb.readU8(); 371 rfb.mark(16);
370 System.out.println("msgType=" + msgType); 372 int mesageType = rfb.readU8();
371 373 System.out.println("mesageType=" + mesageType);
372 rfb.skipBytes(11); 374 rfb.skipBytes(11);
373 int encoding = rfb.readU32(); 375 int encoding = rfb.readU32();
374 System.out.println("encoding=" + encoding); 376 System.out.println("encoding=" + encoding);
375 nBytes = rfb.readU32(); 377 rfb.reset();
376 System.out.println("nBytes=" + nBytes); 378
377 rfb.reset(); 379 rfb.mark(rfb.available());
378 380
379 int len = rfb.available(); 381 rfb.printNumBytesRead();
380 System.out.println("rfb.available()=" + len); 382 int bufSize = (int)rfb.getNumBytesRead();
381 if (len > 0) { 383
382 384 // Read message type from the server.
383 if (nBytes > 0 & encoding == 16) {// 0より大きい(データがある)ときデータを転送 385 int msgType = rfb.readServerMessageType();
384 386
385 rfb.mark(nBytes + 20); 387 // Process the message depending on its type.
386 388 switch (msgType) {
387 byte b[] = new byte[nBytes + 20]; 389 case RfbProto.FramebufferUpdate:
388 // byte b[] = new byte[18+rfb.rnBytes]; 390
389 System.out.println("b.length=" + b.length); 391 if (statNumUpdates == viewer.debugStatsExcludeUpdates
390 392 && !statsRestarted) {
391 rfb.readFully(b); 393 resetStats();
392 394 statsRestarted = true;
393 // rfb.cliSock.getOutputStream().write(b, 0, 395 } else if (statNumUpdates == viewer.debugStatsMeasureUpdates
394 // b.length); 396 && statsRestarted) {
395 rfb.sendData(b); 397 viewer.disconnect();
396 } 398 }
397 } 399
398 } 400 rfb.readFramebufferUpdate();
399 } 401 statNumUpdates++;
400 byte buffer[] = new byte[rfb.available()]; 402
401 rfb.readFully(buffer); 403 boolean cursorPosReceived = false;
402 rfb.sendData(buffer); 404
403 405 for (int i = 0; i < rfb.updateNRects; i++) {
404 count++; 406
405 407 rfb.readFramebufferUpdateRectHdr();
408 statNumTotalRects++;
409 int rx = rfb.updateRectX, ry = rfb.updateRectY;
410 int rw = rfb.updateRectW, rh = rfb.updateRectH;
411
412 if (rfb.updateRectEncoding == rfb.EncodingLastRect)
413 break;
414
415 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
416 rfb.setFramebufferSize(rw, rh);
417 updateFramebufferSize();
418 break;
419 }
420
421 if (rfb.updateRectEncoding == rfb.EncodingXCursor
422 || rfb.updateRectEncoding == rfb.EncodingRichCursor) {
423 handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry,
424 rw, rh);
425 continue;
426 }
427
428 if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
429 softCursorMove(rx, ry);
430 cursorPosReceived = true;
431 continue;
432 }
433
434 long numBytesReadBefore = rfb.getNumBytesRead();
435
436 rfb.startTiming();
437
438 switch (rfb.updateRectEncoding) {
439 case RfbProto.EncodingRaw:
440 statNumRectsRaw++;
441 handleRawRect(rx, ry, rw, rh);
442 rfb.printNumBytesRead();
443 break;
444 case RfbProto.EncodingCopyRect:
445 statNumRectsCopy++;
446 handleCopyRect(rx, ry, rw, rh);
447 break;
448 case RfbProto.EncodingRRE:
449 handleRRERect(rx, ry, rw, rh);
450 break;
451 case RfbProto.EncodingCoRRE:
452 handleCoRRERect(rx, ry, rw, rh);
453 break;
454 case RfbProto.EncodingHextile:
455 statNumRectsHextile++;
456 handleHextileRect(rx, ry, rw, rh);
457 break;
458 case RfbProto.EncodingZRLE:
459 statNumRectsZRLE++;
460 handleZRLERect(rx, ry, rw, rh);
461 break;
462 case RfbProto.EncodingZlib:
463 handleZlibRect(rx, ry, rw, rh);
464 break;
465 case RfbProto.EncodingTight:
466 statNumRectsTight++;
467 handleTightRect(rx, ry, rw, rh);
468 break;
469 default:
470 throw new Exception("Unknown RFB rectangle encoding "
471 + rfb.updateRectEncoding);
472 }
473
474 rfb.stopTiming();
475
476 statNumPixelRects++;
477 statNumBytesDecoded += rw * rh * bytesPixel;
478 statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore);
479 }
480
481 boolean fullUpdateNeeded = false;
482
483 // Start/stop session recording if necessary. Request full
484 // update if a new session file was opened.
485 if (viewer.checkRecordingStatus())
486 fullUpdateNeeded = true;
487
488 // Defer framebuffer update request if necessary. But wake up
489 // immediately on keyboard or mouse event. Also, don't sleep
490 // if there is some data to receive, or if the last update
491 // included a PointerPos message.
492 if (viewer.deferUpdateRequests > 0 && rfb.available() == 0
493 && !cursorPosReceived) {
494 synchronized (rfb) {
495 try {
496 rfb.wait(viewer.deferUpdateRequests);
497 } catch (InterruptedException e) {
498 }
499 }
500 }
501
502 viewer.autoSelectEncodings();
503
504 // Before requesting framebuffer update, check if the pixel
505 // format should be changed.
406 /* 506 /*
407 // Read message type from the server. 507 if (viewer.options.eightBitColors != (bytesPixel == 1)) {
408 int msgType = rfb.readServerMessageType(); 508 // Pixel format should be changed.
409 509 setPixelFormat();
410 // Process the message depending on its type. 510 fullUpdateNeeded = true;
411 switch (msgType) { 511 }
412 case RfbProto.FramebufferUpdate: 512 */
413 513 // Request framebuffer update if needed.
414 if (statNumUpdates == viewer.debugStatsExcludeUpdates 514 int w = rfb.framebufferWidth;
415 && !statsRestarted) { 515 int h = rfb.framebufferHeight;
416 resetStats(); 516 rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
417 statsRestarted = true; 517
418 } else if (statNumUpdates == viewer.debugStatsMeasureUpdates 518 break;
419 && statsRestarted) { 519
420 viewer.disconnect(); 520 case RfbProto.SetColourMapEntries:
421 } 521 throw new Exception("Can't handle SetColourMapEntries message");
422 522
423 rfb.readFramebufferUpdate(); 523 case RfbProto.Bell:
424 statNumUpdates++; 524 Toolkit.getDefaultToolkit().beep();
425 525 break;
426 boolean cursorPosReceived = false; 526
427 527 case RfbProto.ServerCutText:
428 for (int i = 0; i < rfb.updateNRects; i++) { 528 String s = rfb.readServerCutText();
429 529 viewer.clipboard.setCutText(s);
430 rfb.readFramebufferUpdateRectHdr(); 530 break;
431 statNumTotalRects++; 531
432 int rx = rfb.updateRectX, ry = rfb.updateRectY; 532 default:
433 int rw = rfb.updateRectW, rh = rfb.updateRectH; 533 throw new Exception("Unknown RFB message type " + msgType);
434 534 }
435 if (rfb.updateRectEncoding == rfb.EncodingLastRect) 535 bufSize = (int)rfb.getNumBytesRead() - bufSize;
436 break; 536 rfb.bufResetSend(bufSize);
437 537 }
438 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { 538 }
439 rfb.setFramebufferSize(rw, rh); 539
440 updateFramebufferSize(); 540 //
441 break; 541 // Handle a raw rectangle. The second form with paint==false is used
442 } 542 // by the Hextile decoder for raw-encoded tiles.
443 543 //
444 if (rfb.updateRectEncoding == rfb.EncodingXCursor 544
445 || rfb.updateRectEncoding == rfb.EncodingRichCursor) { 545 void handleRawRect(int x, int y, int w, int h) throws IOException {
446 handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, 546 handleRawRect(x, y, w, h, true);
447 rw, rh); 547 }
548
549 void handleRawRect(int x, int y, int w, int h, boolean paint)
550 throws IOException {
551
552 if (bytesPixel == 1) {
553 for (int dy = y; dy < y + h; dy++) {
554 rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
555 if (rfb.rec != null) {
556 rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
557 }
558 }
559 } else {
560 byte[] buf = new byte[w * 4];
561 int i, offset;
562 for (int dy = y; dy < y + h; dy++) {
563 rfb.readFully(buf);
564 if (rfb.rec != null) {
565 rfb.rec.write(buf);
566 }
567 /*
568 * offset = dy * rfb.framebufferWidth + x; for (i = 0; i < w;
569 * i++) { pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 |
570 * (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); }
571 */
572 }
573 }
574 /*
575 * handleUpdatedPixels(x, y, w, h); if (paint) scheduleRepaint(x, y, w,
576 * h);
577 */
578 }
579
580 //
581 // Handle a CopyRect rectangle.
582 //
583
584 void handleCopyRect(int x, int y, int w, int h) throws IOException {
585
586 rfb.readCopyRect();
587 memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
588 - rfb.copyRectSrcX, y - rfb.copyRectSrcY);
589
590 scheduleRepaint(x, y, w, h);
591 }
592
593 //
594 // Handle an RRE-encoded rectangle.
595 //
596
597 void handleRRERect(int x, int y, int w, int h) throws IOException {
598
599 int nSubrects = rfb.readU32();
600
601 byte[] bg_buf = new byte[bytesPixel];
602 rfb.readFully(bg_buf);
603 Color pixel;
604 if (bytesPixel == 1) {
605 pixel = colors[bg_buf[0] & 0xFF];
606 } else {
607 pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
608 bg_buf[0] & 0xFF);
609 }
610 memGraphics.setColor(pixel);
611 memGraphics.fillRect(x, y, w, h);
612
613 byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
614 rfb.readFully(buf);
615 DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
616
617 if (rfb.rec != null) {
618 rfb.rec.writeIntBE(nSubrects);
619 rfb.rec.write(bg_buf);
620 rfb.rec.write(buf);
621 }
622
623 int sx, sy, sw, sh;
624
625 for (int j = 0; j < nSubrects; j++) {
626 if (bytesPixel == 1) {
627 pixel = colors[ds.readUnsignedByte()];
628 } else {
629 ds.skip(4);
630 pixel = new Color(buf[j * 12 + 2] & 0xFF,
631 buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF);
632 }
633 sx = x + ds.readUnsignedShort();
634 sy = y + ds.readUnsignedShort();
635 sw = ds.readUnsignedShort();
636 sh = ds.readUnsignedShort();
637
638 memGraphics.setColor(pixel);
639 memGraphics.fillRect(sx, sy, sw, sh);
640 }
641
642 scheduleRepaint(x, y, w, h);
643 }
644
645 //
646 // Handle a CoRRE-encoded rectangle.
647 //
648
649 void handleCoRRERect(int x, int y, int w, int h) throws IOException {
650 int nSubrects = rfb.readU32();
651
652 byte[] bg_buf = new byte[bytesPixel];
653 rfb.readFully(bg_buf);
654 Color pixel;
655 if (bytesPixel == 1) {
656 pixel = colors[bg_buf[0] & 0xFF];
657 } else {
658 pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
659 bg_buf[0] & 0xFF);
660 }
661 memGraphics.setColor(pixel);
662 memGraphics.fillRect(x, y, w, h);
663
664 byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
665 rfb.readFully(buf);
666
667 if (rfb.rec != null) {
668 rfb.rec.writeIntBE(nSubrects);
669 rfb.rec.write(bg_buf);
670 rfb.rec.write(buf);
671 }
672
673 int sx, sy, sw, sh;
674 int i = 0;
675
676 for (int j = 0; j < nSubrects; j++) {
677 if (bytesPixel == 1) {
678 pixel = colors[buf[i++] & 0xFF];
679 } else {
680 pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
681 buf[i] & 0xFF);
682 i += 4;
683 }
684 sx = x + (buf[i++] & 0xFF);
685 sy = y + (buf[i++] & 0xFF);
686 sw = buf[i++] & 0xFF;
687 sh = buf[i++] & 0xFF;
688
689 memGraphics.setColor(pixel);
690 memGraphics.fillRect(sx, sy, sw, sh);
691 }
692
693 scheduleRepaint(x, y, w, h);
694 }
695
696 //
697 // Handle a Hextile-encoded rectangle.
698 //
699
700 // These colors should be kept between handleHextileSubrect() calls.
701 private Color hextile_bg, hextile_fg;
702
703 void handleHextileRect(int x, int y, int w, int h) throws IOException {
704
705 hextile_bg = new Color(0);
706 hextile_fg = new Color(0);
707
708 for (int ty = y; ty < y + h; ty += 16) {
709 int th = 16;
710 if (y + h - ty < 16)
711 th = y + h - ty;
712
713 for (int tx = x; tx < x + w; tx += 16) {
714 int tw = 16;
715 if (x + w - tx < 16)
716 tw = x + w - tx;
717
718 handleHextileSubrect(tx, ty, tw, th);
719 }
720
721 // Finished with a row of tiles, now let's show it.
722 scheduleRepaint(x, y, w, h);
723 }
724 }
725
726 //
727 // Handle one tile in the Hextile-encoded data.
728 //
729
730 void handleHextileSubrect(int tx, int ty, int tw, int th)
731 throws IOException {
732
733 int subencoding = rfb.readU8();
734 if (rfb.rec != null) {
735 rfb.rec.writeByte(subencoding);
736 }
737
738 // Is it a raw-encoded sub-rectangle?
739 if ((subencoding & rfb.HextileRaw) != 0) {
740 handleRawRect(tx, ty, tw, th, false);
741 return;
742 }
743
744 // Read and draw the background if specified.
745 byte[] cbuf = new byte[bytesPixel];
746 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
747 rfb.readFully(cbuf);
748 if (bytesPixel == 1) {
749 hextile_bg = colors[cbuf[0] & 0xFF];
750 } else {
751 hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
752 cbuf[0] & 0xFF);
753 }
754 if (rfb.rec != null) {
755 rfb.rec.write(cbuf);
756 }
757 }
758 memGraphics.setColor(hextile_bg);
759 memGraphics.fillRect(tx, ty, tw, th);
760
761 // Read the foreground color if specified.
762 if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
763 rfb.readFully(cbuf);
764 if (bytesPixel == 1) {
765 hextile_fg = colors[cbuf[0] & 0xFF];
766 } else {
767 hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF,
768 cbuf[0] & 0xFF);
769 }
770 if (rfb.rec != null) {
771 rfb.rec.write(cbuf);
772 }
773 }
774
775 // Done with this tile if there is no sub-rectangles.
776 if ((subencoding & rfb.HextileAnySubrects) == 0)
777 return;
778
779 int nSubrects = rfb.readU8();
780 int bufsize = nSubrects * 2;
781 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
782 bufsize += nSubrects * bytesPixel;
783 }
784 byte[] buf = new byte[bufsize];
785 rfb.readFully(buf);
786 if (rfb.rec != null) {
787 rfb.rec.writeByte(nSubrects);
788 rfb.rec.write(buf);
789 }
790
791 int b1, b2, sx, sy, sw, sh;
792 int i = 0;
793
794 if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
795
796 // Sub-rectangles are all of the same color.
797 memGraphics.setColor(hextile_fg);
798 for (int j = 0; j < nSubrects; j++) {
799 b1 = buf[i++] & 0xFF;
800 b2 = buf[i++] & 0xFF;
801 sx = tx + (b1 >> 4);
802 sy = ty + (b1 & 0xf);
803 sw = (b2 >> 4) + 1;
804 sh = (b2 & 0xf) + 1;
805 memGraphics.fillRect(sx, sy, sw, sh);
806 }
807 } else if (bytesPixel == 1) {
808
809 // BGR233 (8-bit color) version for colored sub-rectangles.
810 for (int j = 0; j < nSubrects; j++) {
811 hextile_fg = colors[buf[i++] & 0xFF];
812 b1 = buf[i++] & 0xFF;
813 b2 = buf[i++] & 0xFF;
814 sx = tx + (b1 >> 4);
815 sy = ty + (b1 & 0xf);
816 sw = (b2 >> 4) + 1;
817 sh = (b2 & 0xf) + 1;
818 memGraphics.setColor(hextile_fg);
819 memGraphics.fillRect(sx, sy, sw, sh);
820 }
821
822 } else {
823
824 // Full-color (24-bit) version for colored sub-rectangles.
825 for (int j = 0; j < nSubrects; j++) {
826 hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
827 buf[i] & 0xFF);
828 i += 4;
829 b1 = buf[i++] & 0xFF;
830 b2 = buf[i++] & 0xFF;
831 sx = tx + (b1 >> 4);
832 sy = ty + (b1 & 0xf);
833 sw = (b2 >> 4) + 1;
834 sh = (b2 & 0xf) + 1;
835 memGraphics.setColor(hextile_fg);
836 memGraphics.fillRect(sx, sy, sw, sh);
837 }
838
839 }
840 }
841
842 //
843 // Handle a ZRLE-encoded rectangle.
844 //
845 // FIXME: Currently, session recording is not fully supported for ZRLE.
846 //
847
848 void handleZRLERect(int x, int y, int w, int h) throws Exception {
849
850 if (zrleInStream == null)
851 zrleInStream = new ZlibInStream();
852
853 int nBytes = rfb.readU32();
854 if (nBytes > 64 * 1024 * 1024)
855 throw new Exception("ZRLE decoder: illegal compressed data size");
856
857 if (zrleBuf == null || zrleBufLen < nBytes) {
858 zrleBufLen = nBytes + 4096;
859 zrleBuf = new byte[zrleBufLen];
860 }
861
862 // FIXME: Do not wait for all the data before decompression.
863 rfb.readFully(zrleBuf, 0, nBytes);
864
865 if (rfb.rec != null) {
866 if (rfb.recordFromBeginning) {
867 rfb.rec.writeIntBE(nBytes);
868 rfb.rec.write(zrleBuf, 0, nBytes);
869 } else if (!zrleRecWarningShown) {
870 System.out.println("Warning: ZRLE session can be recorded"
871 + " only from the beginning");
872 System.out.println("Warning: Recorded file may be corrupted");
873 zrleRecWarningShown = true;
874 }
875
876 }
877
878 zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
879
880 for (int ty = y; ty < y + h; ty += 64) {
881
882 int th = Math.min(y + h - ty, 64);
883
884 for (int tx = x; tx < x + w; tx += 64) {
885
886 int tw = Math.min(x + w - tx, 64);
887
888 int mode = zrleInStream.readU8();
889 boolean rle = (mode & 128) != 0;
890 int palSize = mode & 127;
891 int[] palette = new int[128];
892
893 readZrlePalette(palette, palSize);
894
895 if (palSize == 1) {
896 int pix = palette[0];
897 Color c = (bytesPixel == 1) ? colors[pix] : new Color(
898 0xFF000000 | pix);
899 memGraphics.setColor(c);
900 memGraphics.fillRect(tx, ty, tw, th);
448 continue; 901 continue;
449 } 902 }
450 903
451 if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { 904 if (!rle) {
452 softCursorMove(rx, ry); 905 if (palSize == 0) {
453 cursorPosReceived = true; 906 readZrleRawPixels(tw, th);
454 continue; 907 } else {
455 } 908 readZrlePackedPixels(tw, th, palette, palSize);
456 909 }
457 long numBytesReadBefore = rfb.getNumBytesRead(); 910 } else {
458 911 if (palSize == 0) {
459 rfb.startTiming(); 912 readZrlePlainRLEPixels(tw, th);
460 913 } else {
461 switch (rfb.updateRectEncoding) { 914 readZrlePackedRLEPixels(tw, th, palette);
462 case RfbProto.EncodingRaw: 915 }
463 statNumRectsRaw++; 916 }
464 handleRawRect(rx, ry, rw, rh); 917 handleUpdatedZrleTile(tx, ty, tw, th);
465 break; 918 }
466 case RfbProto.EncodingCopyRect: 919 }
467 statNumRectsCopy++; 920
468 handleCopyRect(rx, ry, rw, rh); 921 zrleInStream.reset();
469 break; 922
470 case RfbProto.EncodingRRE:
471 handleRRERect(rx, ry, rw, rh);
472 break;
473 case RfbProto.EncodingCoRRE:
474 handleCoRRERect(rx, ry, rw, rh);
475 break;
476 case RfbProto.EncodingHextile:
477 statNumRectsHextile++;
478 handleHextileRect(rx, ry, rw, rh);
479 break;
480 case RfbProto.EncodingZRLE:
481 statNumRectsZRLE++;
482 handleZRLERect(rx, ry, rw, rh);
483 break;
484 case RfbProto.EncodingZlib:
485 handleZlibRect(rx, ry, rw, rh);
486 break;
487 case RfbProto.EncodingTight:
488 statNumRectsTight++;
489 handleTightRect(rx, ry, rw, rh);
490 break;
491 default:
492 throw new Exception("Unknown RFB rectangle encoding "
493 + rfb.updateRectEncoding);
494 }
495
496 rfb.stopTiming();
497
498 statNumPixelRects++;
499 statNumBytesDecoded += rw * rh * bytesPixel;
500 statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore);
501 }
502
503 boolean fullUpdateNeeded = false;
504
505 // Start/stop session recording if necessary. Request full
506 // update if a new session file was opened.
507 if (viewer.checkRecordingStatus())
508 fullUpdateNeeded = true;
509
510 // Defer framebuffer update request if necessary. But wake up
511 // immediately on keyboard or mouse event. Also, don't sleep
512 // if there is some data to receive, or if the last update
513 // included a PointerPos message.
514 if (viewer.deferUpdateRequests > 0 && rfb.available() == 0
515 && !cursorPosReceived) {
516 synchronized (rfb) {
517 try {
518 rfb.wait(viewer.deferUpdateRequests);
519 } catch (InterruptedException e) {
520 }
521 }
522 }
523
524 viewer.autoSelectEncodings();
525
526 // Before requesting framebuffer update, check if the pixel
527 // format should be changed.
528
529 if (viewer.options.eightBitColors != (bytesPixel == 1)) {
530 // Pixel format should be changed.
531 setPixelFormat();
532 fullUpdateNeeded = true;
533 }
534
535
536 // Request framebuffer update if needed.
537 int w = rfb.framebufferWidth;
538 int h = rfb.framebufferHeight;
539 rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded);
540
541 break;
542
543 case RfbProto.SetColourMapEntries:
544 throw new Exception("Can't handle SetColourMapEntries message");
545
546 case RfbProto.Bell:
547 Toolkit.getDefaultToolkit().beep();
548 break;
549
550 case RfbProto.ServerCutText:
551 String s = rfb.readServerCutText();
552 viewer.clipboard.setCutText(s);
553 break;
554
555 default:
556 throw new Exception("Unknown RFB message type " + msgType);
557 }
558 */
559 }
560 }
561
562 //
563 // Handle a raw rectangle. The second form with paint==false is used
564 // by the Hextile decoder for raw-encoded tiles.
565 //
566
567 void handleRawRect(int x, int y, int w, int h) throws IOException {
568 handleRawRect(x, y, w, h, true);
569 }
570
571 void handleRawRect(int x, int y, int w, int h, boolean paint)
572 throws IOException {
573
574 if (bytesPixel == 1) {
575 for (int dy = y; dy < y + h; dy++) {
576 rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
577 if (rfb.rec != null) {
578 rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
579 }
580 }
581 } else {
582 byte[] buf = new byte[w * 4];
583 int i, offset;
584 for (int dy = y; dy < y + h; dy++) {
585 rfb.readFully(buf);
586 if (rfb.rec != null) {
587 rfb.rec.write(buf);
588 }
589 /*
590 offset = dy * rfb.framebufferWidth + x;
591 for (i = 0; i < w; i++) {
592 pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
593 | (buf[i * 4 + 1] & 0xFF) << 8
594 | (buf[i * 4] & 0xFF);
595 }
596 */
597 }
598 }
599 /*
600 handleUpdatedPixels(x, y, w, h);
601 if (paint)
602 scheduleRepaint(x, y, w, h); 923 scheduleRepaint(x, y, w, h);
603 */ 924 }
604 } 925
605 926 int readPixel(InStream is) throws Exception {
606 // 927 int pix;
607 // Handle a CopyRect rectangle. 928
608 //
609
610 void handleCopyRect(int x, int y, int w, int h) throws IOException {
611
612 rfb.readCopyRect();
613 memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x
614 - rfb.copyRectSrcX, y - rfb.copyRectSrcY);
615
616 scheduleRepaint(x, y, w, h);
617 }
618
619 //
620 // Handle an RRE-encoded rectangle.
621 //
622
623 void handleRRERect(int x, int y, int w, int h) throws IOException {
624
625 int nSubrects = rfb.readU32();
626
627 byte[] bg_buf = new byte[bytesPixel];
628 rfb.readFully(bg_buf);
629 Color pixel;
630 if (bytesPixel == 1) {
631 pixel = colors[bg_buf[0] & 0xFF];
632 } else {
633 pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
634 bg_buf[0] & 0xFF);
635 }
636 memGraphics.setColor(pixel);
637 memGraphics.fillRect(x, y, w, h);
638
639 byte[] buf = new byte[nSubrects * (bytesPixel + 8)];
640 rfb.readFully(buf);
641 DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf));
642
643 if (rfb.rec != null) {
644 rfb.rec.writeIntBE(nSubrects);
645 rfb.rec.write(bg_buf);
646 rfb.rec.write(buf);
647 }
648
649 int sx, sy, sw, sh;
650
651 for (int j = 0; j < nSubrects; j++) {
652 if (bytesPixel == 1) { 929 if (bytesPixel == 1) {
653 pixel = colors[ds.readUnsignedByte()]; 930
931 pix = is.readU8();
654 } else { 932 } else {
655 ds.skip(4); 933 int p1 = is.readU8();
656 pixel = new Color(buf[j * 12 + 2] & 0xFF, 934 int p2 = is.readU8();
657 buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); 935 int p3 = is.readU8();
658 } 936 pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
659 sx = x + ds.readUnsignedShort(); 937 }
660 sy = y + ds.readUnsignedShort(); 938 return pix;
661 sw = ds.readUnsignedShort(); 939 }
662 sh = ds.readUnsignedShort(); 940
663 941 void readPixels(InStream is, int[] dst, int count) throws Exception {
664 memGraphics.setColor(pixel); 942 int pix;
665 memGraphics.fillRect(sx, sy, sw, sh);
666 }
667
668 scheduleRepaint(x, y, w, h);
669 }
670
671 //
672 // Handle a CoRRE-encoded rectangle.
673 //
674
675 void handleCoRRERect(int x, int y, int w, int h) throws IOException {
676 int nSubrects = rfb.readU32();
677
678 byte[] bg_buf = new byte[bytesPixel];
679 rfb.readFully(bg_buf);
680 Color pixel;
681 if (bytesPixel == 1) {
682 pixel = colors[bg_buf[0] & 0xFF];
683 } else {
684 pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF,
685 bg_buf[0] & 0xFF);
686 }
687 memGraphics.setColor(pixel);
688 memGraphics.fillRect(x, y, w, h);
689
690 byte[] buf = new byte[nSubrects * (bytesPixel + 4)];
691 rfb.readFully(buf);
692
693 if (rfb.rec != null) {
694 rfb.rec.writeIntBE(nSubrects);
695 rfb.rec.write(bg_buf);
696 rfb.rec.write(buf);
697 }
698
699 int sx, sy, sw, sh;
700 int i = 0;
701
702 for (int j = 0; j < nSubrects; j++) {
703 if (bytesPixel == 1) { 943 if (bytesPixel == 1) {
704 pixel = colors[buf[i++] & 0xFF]; 944 byte[] buf = new byte[count];
945 is.readBytes(buf, 0, count);
946 for (int i = 0; i < count; i++) {
947 dst[i] = (int) buf[i] & 0xFF;
948 }
705 } else { 949 } else {
706 pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, 950 byte[] buf = new byte[count * 3];
707 buf[i] & 0xFF); 951 is.readBytes(buf, 0, count * 3);
708 i += 4; 952 for (int i = 0; i < count; i++) {
709 } 953 dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
710 sx = x + (buf[i++] & 0xFF); 954 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
711 sy = y + (buf[i++] & 0xFF); 955 }
712 sw = buf[i++] & 0xFF; 956 }
713 sh = buf[i++] & 0xFF; 957 }
714 958
715 memGraphics.setColor(pixel); 959 void readZrlePalette(int[] palette, int palSize) throws Exception {
716 memGraphics.fillRect(sx, sy, sw, sh); 960 readPixels(zrleInStream, palette, palSize);
717 } 961 }
718 962
719 scheduleRepaint(x, y, w, h); 963 void readZrleRawPixels(int tw, int th) throws Exception {
720 }
721
722 //
723 // Handle a Hextile-encoded rectangle.
724 //
725
726 // These colors should be kept between handleHextileSubrect() calls.
727 private Color hextile_bg, hextile_fg;
728
729 void handleHextileRect(int x, int y, int w, int h) throws IOException {
730
731 hextile_bg = new Color(0);
732 hextile_fg = new Color(0);
733
734 for (int ty = y; ty < y + h; ty += 16) {
735 int th = 16;
736 if (y + h - ty < 16)
737 th = y + h - ty;
738
739 for (int tx = x; tx < x + w; tx += 16) {
740 int tw = 16;
741 if (x + w - tx < 16)
742 tw = x + w - tx;
743
744 handleHextileSubrect(tx, ty, tw, th);
745 }
746
747 // Finished with a row of tiles, now let's show it.
748 scheduleRepaint(x, y, w, h);
749 }
750 }
751
752 //
753 // Handle one tile in the Hextile-encoded data.
754 //
755
756 void handleHextileSubrect(int tx, int ty, int tw, int th)
757 throws IOException {
758
759 int subencoding = rfb.readU8();
760 if (rfb.rec != null) {
761 rfb.rec.writeByte(subencoding);
762 }
763
764 // Is it a raw-encoded sub-rectangle?
765 if ((subencoding & rfb.HextileRaw) != 0) {
766 handleRawRect(tx, ty, tw, th, false);
767 return;
768 }
769
770 // Read and draw the background if specified.
771 byte[] cbuf = new byte[bytesPixel];
772 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
773 rfb.readFully(cbuf);
774 if (bytesPixel == 1) { 964 if (bytesPixel == 1) {
775 hextile_bg = colors[cbuf[0] & 0xFF]; 965 zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
776 } else { 966 } else {
777 hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, 967 readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
778 cbuf[0] & 0xFF); 968 }
779 } 969 }
780 if (rfb.rec != null) { 970
781 rfb.rec.write(cbuf); 971 void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
782 } 972 throws Exception {
783 } 973
784 memGraphics.setColor(hextile_bg); 974 int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
785 memGraphics.fillRect(tx, ty, tw, th); 975 : ((palSize > 2) ? 2 : 1)));
786 976 int ptr = 0;
787 // Read the foreground color if specified. 977
788 if ((subencoding & rfb.HextileForegroundSpecified) != 0) { 978 for (int i = 0; i < th; i++) {
789 rfb.readFully(cbuf); 979 int eol = ptr + tw;
790 if (bytesPixel == 1) { 980 int b = 0;
791 hextile_fg = colors[cbuf[0] & 0xFF]; 981 int nbits = 0;
792 } else { 982
793 hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, 983 while (ptr < eol) {
794 cbuf[0] & 0xFF); 984 if (nbits == 0) {
795 } 985 b = zrleInStream.readU8();
796 if (rfb.rec != null) { 986 nbits = 8;
797 rfb.rec.write(cbuf); 987 }
798 } 988 nbits -= bppp;
799 } 989 int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
800 990 if (bytesPixel == 1) {
801 // Done with this tile if there is no sub-rectangles. 991 zrleTilePixels8[ptr++] = (byte) palette[index];
802 if ((subencoding & rfb.HextileAnySubrects) == 0)
803 return;
804
805 int nSubrects = rfb.readU8();
806 int bufsize = nSubrects * 2;
807 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
808 bufsize += nSubrects * bytesPixel;
809 }
810 byte[] buf = new byte[bufsize];
811 rfb.readFully(buf);
812 if (rfb.rec != null) {
813 rfb.rec.writeByte(nSubrects);
814 rfb.rec.write(buf);
815 }
816
817 int b1, b2, sx, sy, sw, sh;
818 int i = 0;
819
820 if ((subencoding & rfb.HextileSubrectsColoured) == 0) {
821
822 // Sub-rectangles are all of the same color.
823 memGraphics.setColor(hextile_fg);
824 for (int j = 0; j < nSubrects; j++) {
825 b1 = buf[i++] & 0xFF;
826 b2 = buf[i++] & 0xFF;
827 sx = tx + (b1 >> 4);
828 sy = ty + (b1 & 0xf);
829 sw = (b2 >> 4) + 1;
830 sh = (b2 & 0xf) + 1;
831 memGraphics.fillRect(sx, sy, sw, sh);
832 }
833 } else if (bytesPixel == 1) {
834
835 // BGR233 (8-bit color) version for colored sub-rectangles.
836 for (int j = 0; j < nSubrects; j++) {
837 hextile_fg = colors[buf[i++] & 0xFF];
838 b1 = buf[i++] & 0xFF;
839 b2 = buf[i++] & 0xFF;
840 sx = tx + (b1 >> 4);
841 sy = ty + (b1 & 0xf);
842 sw = (b2 >> 4) + 1;
843 sh = (b2 & 0xf) + 1;
844 memGraphics.setColor(hextile_fg);
845 memGraphics.fillRect(sx, sy, sw, sh);
846 }
847
848 } else {
849
850 // Full-color (24-bit) version for colored sub-rectangles.
851 for (int j = 0; j < nSubrects; j++) {
852 hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF,
853 buf[i] & 0xFF);
854 i += 4;
855 b1 = buf[i++] & 0xFF;
856 b2 = buf[i++] & 0xFF;
857 sx = tx + (b1 >> 4);
858 sy = ty + (b1 & 0xf);
859 sw = (b2 >> 4) + 1;
860 sh = (b2 & 0xf) + 1;
861 memGraphics.setColor(hextile_fg);
862 memGraphics.fillRect(sx, sy, sw, sh);
863 }
864
865 }
866 }
867
868 //
869 // Handle a ZRLE-encoded rectangle.
870 //
871 // FIXME: Currently, session recording is not fully supported for ZRLE.
872 //
873
874 void handleZRLERect(int x, int y, int w, int h) throws Exception {
875
876 if (zrleInStream == null)
877 zrleInStream = new ZlibInStream();
878
879 int nBytes = rfb.readU32();
880 if (nBytes > 64 * 1024 * 1024)
881 throw new Exception("ZRLE decoder: illegal compressed data size");
882
883 if (zrleBuf == null || zrleBufLen < nBytes) {
884 zrleBufLen = nBytes + 4096;
885 zrleBuf = new byte[zrleBufLen];
886 }
887
888 // FIXME: Do not wait for all the data before decompression.
889 rfb.readFully(zrleBuf, 0, nBytes);
890
891 if (rfb.rec != null) {
892 if (rfb.recordFromBeginning) {
893 rfb.rec.writeIntBE(nBytes);
894 rfb.rec.write(zrleBuf, 0, nBytes);
895 } else if (!zrleRecWarningShown) {
896 System.out.println("Warning: ZRLE session can be recorded"
897 + " only from the beginning");
898 System.out.println("Warning: Recorded file may be corrupted");
899 zrleRecWarningShown = true;
900 }
901
902 }
903
904 zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
905
906 for (int ty = y; ty < y + h; ty += 64) {
907
908 int th = Math.min(y + h - ty, 64);
909
910 for (int tx = x; tx < x + w; tx += 64) {
911
912 int tw = Math.min(x + w - tx, 64);
913
914 int mode = zrleInStream.readU8();
915 boolean rle = (mode & 128) != 0;
916 int palSize = mode & 127;
917 int[] palette = new int[128];
918
919 readZrlePalette(palette, palSize);
920
921 if (palSize == 1) {
922 int pix = palette[0];
923 Color c = (bytesPixel == 1) ? colors[pix] : new Color(
924 0xFF000000 | pix);
925 memGraphics.setColor(c);
926 memGraphics.fillRect(tx, ty, tw, th);
927 continue;
928 }
929
930 if (!rle) {
931 if (palSize == 0) {
932 readZrleRawPixels(tw, th);
933 } else { 992 } else {
934 readZrlePackedPixels(tw, th, palette, palSize); 993 zrleTilePixels24[ptr++] = palette[index];
935 } 994 }
936 } else { 995 }
937 if (palSize == 0) { 996 }
938 readZrlePlainRLEPixels(tw, th); 997 }
939 } else { 998
940 readZrlePackedRLEPixels(tw, th, palette); 999 void readZrlePlainRLEPixels(int tw, int th) throws Exception {
941 } 1000 int ptr = 0;
942 } 1001 int end = ptr + tw * th;
943 handleUpdatedZrleTile(tx, ty, tw, th); 1002 while (ptr < end) {
944 } 1003 int pix = readPixel(zrleInStream);
945 } 1004 int len = 1;
946
947 zrleInStream.reset();
948
949 scheduleRepaint(x, y, w, h);
950 }
951
952 int readPixel(InStream is) throws Exception {
953 int pix;
954
955 if (bytesPixel == 1) {
956
957 pix = is.readU8();
958 } else {
959 int p1 = is.readU8();
960 int p2 = is.readU8();
961 int p3 = is.readU8();
962 pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
963 }
964 return pix;
965 }
966
967 void readPixels(InStream is, int[] dst, int count) throws Exception {
968 int pix;
969 if (bytesPixel == 1) {
970 byte[] buf = new byte[count];
971 is.readBytes(buf, 0, count);
972 for (int i = 0; i < count; i++) {
973 dst[i] = (int) buf[i] & 0xFF;
974 }
975 } else {
976 byte[] buf = new byte[count * 3];
977 is.readBytes(buf, 0, count * 3);
978 for (int i = 0; i < count; i++) {
979 dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16
980 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF));
981 }
982 }
983 }
984
985 void readZrlePalette(int[] palette, int palSize) throws Exception {
986 readPixels(zrleInStream, palette, palSize);
987 }
988
989 void readZrleRawPixels(int tw, int th) throws Exception {
990 if (bytesPixel == 1) {
991 zrleInStream.readBytes(zrleTilePixels8, 0, tw * th);
992 } else {
993 readPixels(zrleInStream, zrleTilePixels24, tw * th); // /
994 }
995 }
996
997 void readZrlePackedPixels(int tw, int th, int[] palette, int palSize)
998 throws Exception {
999
1000 int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4
1001 : ((palSize > 2) ? 2 : 1)));
1002 int ptr = 0;
1003
1004 for (int i = 0; i < th; i++) {
1005 int eol = ptr + tw;
1006 int b = 0;
1007 int nbits = 0;
1008
1009 while (ptr < eol) {
1010 if (nbits == 0) {
1011 b = zrleInStream.readU8();
1012 nbits = 8;
1013 }
1014 nbits -= bppp;
1015 int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
1016 if (bytesPixel == 1) {
1017 zrleTilePixels8[ptr++] = (byte) palette[index];
1018 } else {
1019 zrleTilePixels24[ptr++] = palette[index];
1020 }
1021 }
1022 }
1023 }
1024
1025 void readZrlePlainRLEPixels(int tw, int th) throws Exception {
1026 int ptr = 0;
1027 int end = ptr + tw * th;
1028 while (ptr < end) {
1029 int pix = readPixel(zrleInStream);
1030 int len = 1;
1031 int b;
1032 do {
1033 b = zrleInStream.readU8();
1034 len += b;
1035 } while (b == 255);
1036
1037 if (!(len <= end - ptr))
1038 throw new Exception("ZRLE decoder: assertion failed"
1039 + " (len <= end-ptr)");
1040
1041 if (bytesPixel == 1) {
1042 while (len-- > 0)
1043 zrleTilePixels8[ptr++] = (byte) pix;
1044 } else {
1045 while (len-- > 0)
1046 zrleTilePixels24[ptr++] = pix;
1047 }
1048 }
1049 }
1050
1051 void readZrlePackedRLEPixels(int tw, int th, int[] palette)
1052 throws Exception {
1053
1054 int ptr = 0;
1055 int end = ptr + tw * th;
1056 while (ptr < end) {
1057 int index = zrleInStream.readU8();
1058 int len = 1;
1059 if ((index & 128) != 0) {
1060 int b; 1005 int b;
1061 do { 1006 do {
1062 b = zrleInStream.readU8(); 1007 b = zrleInStream.readU8();
1063 len += b; 1008 len += b;
1064 } while (b == 255); 1009 } while (b == 255);
1065 1010
1066 if (!(len <= end - ptr)) 1011 if (!(len <= end - ptr))
1067 throw new Exception("ZRLE decoder: assertion failed" 1012 throw new Exception("ZRLE decoder: assertion failed"
1068 + " (len <= end - ptr)"); 1013 + " (len <= end-ptr)");
1069 } 1014
1070 1015 if (bytesPixel == 1) {
1071 index &= 127; 1016 while (len-- > 0)
1072 int pix = palette[index]; 1017 zrleTilePixels8[ptr++] = (byte) pix;
1073 1018 } else {
1019 while (len-- > 0)
1020 zrleTilePixels24[ptr++] = pix;
1021 }
1022 }
1023 }
1024
1025 void readZrlePackedRLEPixels(int tw, int th, int[] palette)
1026 throws Exception {
1027
1028 int ptr = 0;
1029 int end = ptr + tw * th;
1030 while (ptr < end) {
1031 int index = zrleInStream.readU8();
1032 int len = 1;
1033 if ((index & 128) != 0) {
1034 int b;
1035 do {
1036 b = zrleInStream.readU8();
1037 len += b;
1038 } while (b == 255);
1039
1040 if (!(len <= end - ptr))
1041 throw new Exception("ZRLE decoder: assertion failed"
1042 + " (len <= end - ptr)");
1043 }
1044
1045 index &= 127;
1046 int pix = palette[index];
1047
1048 if (bytesPixel == 1) {
1049 while (len-- > 0)
1050 zrleTilePixels8[ptr++] = (byte) pix;
1051 } else {
1052 while (len-- > 0)
1053 zrleTilePixels24[ptr++] = pix;
1054 }
1055 }
1056 }
1057
1058 //
1059 // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
1060 //
1061
1062 void handleUpdatedZrleTile(int x, int y, int w, int h) {
1063 Object src, dst;
1074 if (bytesPixel == 1) { 1064 if (bytesPixel == 1) {
1075 while (len-- > 0) 1065 src = zrleTilePixels8;
1076 zrleTilePixels8[ptr++] = (byte) pix; 1066 dst = pixels8;
1077 } else { 1067 } else {
1078 while (len-- > 0) 1068 src = zrleTilePixels24;
1079 zrleTilePixels24[ptr++] = pix; 1069 dst = pixels24;
1080 } 1070 }
1081 } 1071 int offsetSrc = 0;
1082 } 1072 int offsetDst = (y * rfb.framebufferWidth + x);
1083 1073 for (int j = 0; j < h; j++) {
1084 // 1074 System.arraycopy(src, offsetSrc, dst, offsetDst, w);
1085 // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. 1075 offsetSrc += w;
1086 // 1076 offsetDst += rfb.framebufferWidth;
1087 1077 }
1088 void handleUpdatedZrleTile(int x, int y, int w, int h) { 1078 handleUpdatedPixels(x, y, w, h);
1089 Object src, dst; 1079 }
1090 if (bytesPixel == 1) { 1080
1091 src = zrleTilePixels8; 1081 //
1092 dst = pixels8; 1082 // Handle a Zlib-encoded rectangle.
1093 } else { 1083 //
1094 src = zrleTilePixels24; 1084
1095 dst = pixels24; 1085 void handleZlibRect(int x, int y, int w, int h) throws Exception {
1096 } 1086
1097 int offsetSrc = 0; 1087 int nBytes = rfb.readU32();
1098 int offsetDst = (y * rfb.framebufferWidth + x); 1088
1099 for (int j = 0; j < h; j++) { 1089 if (zlibBuf == null || zlibBufLen < nBytes) {
1100 System.arraycopy(src, offsetSrc, dst, offsetDst, w); 1090 zlibBufLen = nBytes * 2;
1101 offsetSrc += w; 1091 zlibBuf = new byte[zlibBufLen];
1102 offsetDst += rfb.framebufferWidth; 1092 }
1103 } 1093
1104 handleUpdatedPixels(x, y, w, h); 1094 rfb.readFully(zlibBuf, 0, nBytes);
1105 } 1095
1106 1096 if (rfb.rec != null && rfb.recordFromBeginning) {
1107 // 1097 rfb.rec.writeIntBE(nBytes);
1108 // Handle a Zlib-encoded rectangle. 1098 rfb.rec.write(zlibBuf, 0, nBytes);
1109 // 1099 }
1110 1100
1111 void handleZlibRect(int x, int y, int w, int h) throws Exception { 1101 if (zlibInflater == null) {
1112 1102 zlibInflater = new Inflater();
1113 int nBytes = rfb.readU32(); 1103 }
1114 1104 zlibInflater.setInput(zlibBuf, 0, nBytes);
1115 if (zlibBuf == null || zlibBufLen < nBytes) { 1105
1116 zlibBufLen = nBytes * 2; 1106 if (bytesPixel == 1) {
1117 zlibBuf = new byte[zlibBufLen]; 1107 for (int dy = y; dy < y + h; dy++) {
1118 } 1108 zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
1119 1109 if (rfb.rec != null && !rfb.recordFromBeginning)
1120 rfb.readFully(zlibBuf, 0, nBytes); 1110 rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
1121 1111 }
1122 if (rfb.rec != null && rfb.recordFromBeginning) {
1123 rfb.rec.writeIntBE(nBytes);
1124 rfb.rec.write(zlibBuf, 0, nBytes);
1125 }
1126
1127 if (zlibInflater == null) {
1128 zlibInflater = new Inflater();
1129 }
1130 zlibInflater.setInput(zlibBuf, 0, nBytes);
1131
1132 if (bytesPixel == 1) {
1133 for (int dy = y; dy < y + h; dy++) {
1134 zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
1135 if (rfb.rec != null && !rfb.recordFromBeginning)
1136 rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
1137 }
1138 } else {
1139 byte[] buf = new byte[w * 4];
1140 int i, offset;
1141 for (int dy = y; dy < y + h; dy++) {
1142 zlibInflater.inflate(buf);
1143 offset = dy * rfb.framebufferWidth + x;
1144 for (i = 0; i < w; i++) {
1145 pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
1146 | (buf[i * 4 + 1] & 0xFF) << 8
1147 | (buf[i * 4] & 0xFF);
1148 }
1149 if (rfb.rec != null && !rfb.recordFromBeginning)
1150 rfb.rec.write(buf);
1151 }
1152 }
1153
1154 handleUpdatedPixels(x, y, w, h);
1155 scheduleRepaint(x, y, w, h);
1156 }
1157
1158 //
1159 // Handle a Tight-encoded rectangle.
1160 //
1161
1162 void handleTightRect(int x, int y, int w, int h) throws Exception {
1163
1164 int comp_ctl = rfb.readU8();
1165 if (rfb.rec != null) {
1166 if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
1167 || comp_ctl == (rfb.TightJpeg << 4)) {
1168 // Send data exactly as received.
1169 rfb.rec.writeByte(comp_ctl);
1170 } else { 1112 } else {
1171 // Tell the decoder to flush each of the four zlib streams. 1113 byte[] buf = new byte[w * 4];
1172 rfb.rec.writeByte(comp_ctl | 0x0F); 1114 int i, offset;
1173 } 1115 for (int dy = y; dy < y + h; dy++) {
1174 } 1116 zlibInflater.inflate(buf);
1175 1117 offset = dy * rfb.framebufferWidth + x;
1176 // Flush zlib streams if we are told by the server to do so. 1118 for (i = 0; i < w; i++) {
1177 for (int stream_id = 0; stream_id < 4; stream_id++) { 1119 pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16
1178 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { 1120 | (buf[i * 4 + 1] & 0xFF) << 8
1179 tightInflaters[stream_id] = null; 1121 | (buf[i * 4] & 0xFF);
1180 } 1122 }
1181 comp_ctl >>= 1; 1123 if (rfb.rec != null && !rfb.recordFromBeginning)
1182 } 1124 rfb.rec.write(buf);
1183 1125 }
1184 // Check correctness of subencoding value. 1126 }
1185 if (comp_ctl > rfb.TightMaxSubencoding) { 1127
1186 throw new Exception("Incorrect tight subencoding: " + comp_ctl); 1128 handleUpdatedPixels(x, y, w, h);
1187 }
1188
1189 // Handle solid-color rectangles.
1190 if (comp_ctl == rfb.TightFill) {
1191
1192 if (bytesPixel == 1) {
1193 int idx = rfb.readU8();
1194 memGraphics.setColor(colors[idx]);
1195 if (rfb.rec != null) {
1196 rfb.rec.writeByte(idx);
1197 }
1198 } else {
1199 byte[] buf = new byte[3];
1200 rfb.readFully(buf);
1201 if (rfb.rec != null) {
1202 rfb.rec.write(buf);
1203 }
1204 Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
1205 | (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
1206 memGraphics.setColor(bg);
1207 }
1208 memGraphics.fillRect(x, y, w, h);
1209 scheduleRepaint(x, y, w, h); 1129 scheduleRepaint(x, y, w, h);
1210 return; 1130 }
1211 1131
1212 } 1132 //
1213 1133 // Handle a Tight-encoded rectangle.
1214 if (comp_ctl == rfb.TightJpeg) { 1134 //
1215 1135
1216 statNumRectsTightJPEG++; 1136 void handleTightRect(int x, int y, int w, int h) throws Exception {
1217 1137
1218 // Read JPEG data. 1138 int comp_ctl = rfb.readU8();
1219 byte[] jpegData = new byte[rfb.readCompactLen()];
1220 rfb.readFully(jpegData);
1221 if (rfb.rec != null) { 1139 if (rfb.rec != null) {
1222 if (!rfb.recordFromBeginning) { 1140 if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4)
1223 rfb.recordCompactLen(jpegData.length); 1141 || comp_ctl == (rfb.TightJpeg << 4)) {
1224 } 1142 // Send data exactly as received.
1225 rfb.rec.write(jpegData); 1143 rfb.rec.writeByte(comp_ctl);
1226 } 1144 } else {
1227 1145 // Tell the decoder to flush each of the four zlib streams.
1228 // Create an Image object from the JPEG data. 1146 rfb.rec.writeByte(comp_ctl | 0x0F);
1229 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData); 1147 }
1230 1148 }
1231 // Remember the rectangle where the image should be drawn. 1149
1232 jpegRect = new Rectangle(x, y, w, h); 1150 // Flush zlib streams if we are told by the server to do so.
1233 1151 for (int stream_id = 0; stream_id < 4; stream_id++) {
1234 // Let the imageUpdate() method do the actual drawing, here just 1152 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
1235 // wait until the image is fully loaded and drawn. 1153 tightInflaters[stream_id] = null;
1236 synchronized (jpegRect) { 1154 }
1237 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, 1155 comp_ctl >>= 1;
1238 this); 1156 }
1239 try { 1157
1240 // Wait no longer than three seconds. 1158 // Check correctness of subencoding value.
1241 jpegRect.wait(3000); 1159 if (comp_ctl > rfb.TightMaxSubencoding) {
1242 } catch (InterruptedException e) { 1160 throw new Exception("Incorrect tight subencoding: " + comp_ctl);
1243 throw new Exception("Interrupted while decoding JPEG image"); 1161 }
1244 } 1162
1245 } 1163 // Handle solid-color rectangles.
1246 1164 if (comp_ctl == rfb.TightFill) {
1247 // Done, jpegRect is not needed any more. 1165
1248 jpegRect = null;
1249 return;
1250
1251 }
1252
1253 // Read filter id and parameters.
1254 int numColors = 0, rowSize = w;
1255 byte[] palette8 = new byte[2];
1256 int[] palette24 = new int[256];
1257 boolean useGradient = false;
1258 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
1259 int filter_id = rfb.readU8();
1260 if (rfb.rec != null) {
1261 rfb.rec.writeByte(filter_id);
1262 }
1263 if (filter_id == rfb.TightFilterPalette) {
1264 numColors = rfb.readU8() + 1;
1265 if (rfb.rec != null) {
1266 rfb.rec.writeByte(numColors - 1);
1267 }
1268 if (bytesPixel == 1) { 1166 if (bytesPixel == 1) {
1269 if (numColors != 2) { 1167 int idx = rfb.readU8();
1270 throw new Exception("Incorrect tight palette size: " 1168 memGraphics.setColor(colors[idx]);
1271 + numColors);
1272 }
1273 rfb.readFully(palette8);
1274 if (rfb.rec != null) { 1169 if (rfb.rec != null) {
1275 rfb.rec.write(palette8); 1170 rfb.rec.writeByte(idx);
1276 } 1171 }
1277 } else { 1172 } else {
1278 byte[] buf = new byte[numColors * 3]; 1173 byte[] buf = new byte[3];
1279 rfb.readFully(buf); 1174 rfb.readFully(buf);
1280 if (rfb.rec != null) { 1175 if (rfb.rec != null) {
1281 rfb.rec.write(buf); 1176 rfb.rec.write(buf);
1282 } 1177 }
1283 for (int i = 0; i < numColors; i++) { 1178 Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16
1284 palette24[i] = ((buf[i * 3] & 0xFF) << 16 1179 | (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
1285 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF)); 1180 memGraphics.setColor(bg);
1286 } 1181 }
1287 } 1182 memGraphics.fillRect(x, y, w, h);
1288 if (numColors == 2) 1183 scheduleRepaint(x, y, w, h);
1289 rowSize = (w + 7) / 8; 1184 return;
1290 } else if (filter_id == rfb.TightFilterGradient) { 1185
1291 useGradient = true; 1186 }
1292 } else if (filter_id != rfb.TightFilterCopy) { 1187
1293 throw new Exception("Incorrect tight filter id: " + filter_id); 1188 if (comp_ctl == rfb.TightJpeg) {
1294 } 1189
1295 } 1190 statNumRectsTightJPEG++;
1296 if (numColors == 0 && bytesPixel == 4) 1191
1297 rowSize *= 3; 1192 // Read JPEG data.
1298 1193 byte[] jpegData = new byte[rfb.readCompactLen()];
1299 // Read, optionally uncompress and decode data. 1194 rfb.readFully(jpegData);
1300 int dataSize = h * rowSize;
1301 if (dataSize < rfb.TightMinToCompress) {
1302 // Data size is small - not compressed with zlib.
1303 if (numColors != 0) {
1304 // Indexed colors.
1305 byte[] indexedData = new byte[dataSize];
1306 rfb.readFully(indexedData);
1307 if (rfb.rec != null) { 1195 if (rfb.rec != null) {
1308 rfb.rec.write(indexedData); 1196 if (!rfb.recordFromBeginning) {
1309 } 1197 rfb.recordCompactLen(jpegData.length);
1310 if (numColors == 2) { 1198 }
1311 // Two colors. 1199 rfb.rec.write(jpegData);
1200 }
1201
1202 // Create an Image object from the JPEG data.
1203 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
1204
1205 // Remember the rectangle where the image should be drawn.
1206 jpegRect = new Rectangle(x, y, w, h);
1207
1208 // Let the imageUpdate() method do the actual drawing, here just
1209 // wait until the image is fully loaded and drawn.
1210 synchronized (jpegRect) {
1211 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1,
1212 this);
1213 try {
1214 // Wait no longer than three seconds.
1215 jpegRect.wait(3000);
1216 } catch (InterruptedException e) {
1217 throw new Exception("Interrupted while decoding JPEG image");
1218 }
1219 }
1220
1221 // Done, jpegRect is not needed any more.
1222 jpegRect = null;
1223 return;
1224
1225 }
1226
1227 // Read filter id and parameters.
1228 int numColors = 0, rowSize = w;
1229 byte[] palette8 = new byte[2];
1230 int[] palette24 = new int[256];
1231 boolean useGradient = false;
1232 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
1233 int filter_id = rfb.readU8();
1234 if (rfb.rec != null) {
1235 rfb.rec.writeByte(filter_id);
1236 }
1237 if (filter_id == rfb.TightFilterPalette) {
1238 numColors = rfb.readU8() + 1;
1239 if (rfb.rec != null) {
1240 rfb.rec.writeByte(numColors - 1);
1241 }
1312 if (bytesPixel == 1) { 1242 if (bytesPixel == 1) {
1313 decodeMonoData(x, y, w, h, indexedData, palette8); 1243 if (numColors != 2) {
1244 throw new Exception("Incorrect tight palette size: "
1245 + numColors);
1246 }
1247 rfb.readFully(palette8);
1248 if (rfb.rec != null) {
1249 rfb.rec.write(palette8);
1250 }
1314 } else { 1251 } else {
1315 decodeMonoData(x, y, w, h, indexedData, palette24); 1252 byte[] buf = new byte[numColors * 3];
1316 }
1317 } else {
1318 // 3..255 colors (assuming bytesPixel == 4).
1319 int i = 0;
1320 for (int dy = y; dy < y + h; dy++) {
1321 for (int dx = x; dx < x + w; dx++) {
1322 pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
1323 }
1324 }
1325 }
1326 } else if (useGradient) {
1327 // "Gradient"-processed data
1328 byte[] buf = new byte[w * h * 3];
1329 rfb.readFully(buf);
1330 if (rfb.rec != null) {
1331 rfb.rec.write(buf);
1332 }
1333 decodeGradientData(x, y, w, h, buf);
1334 } else {
1335 // Raw truecolor data.
1336 if (bytesPixel == 1) {
1337 for (int dy = y; dy < y + h; dy++) {
1338 rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
1339 if (rfb.rec != null) {
1340 rfb.rec.write(pixels8, dy * rfb.framebufferWidth
1341 + x, w);
1342 }
1343 }
1344 } else {
1345 byte[] buf = new byte[w * 3];
1346 int i, offset;
1347 for (int dy = y; dy < y + h; dy++) {
1348 rfb.readFully(buf); 1253 rfb.readFully(buf);
1349 if (rfb.rec != null) { 1254 if (rfb.rec != null) {
1350 rfb.rec.write(buf); 1255 rfb.rec.write(buf);
1351 } 1256 }
1352 offset = dy * rfb.framebufferWidth + x; 1257 for (int i = 0; i < numColors; i++) {
1353 for (i = 0; i < w; i++) { 1258 palette24[i] = ((buf[i * 3] & 0xFF) << 16
1354 pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 1259 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF));
1355 | (buf[i * 3 + 1] & 0xFF) << 8 1260 }
1356 | (buf[i * 3 + 2] & 0xFF); 1261 }
1357 } 1262 if (numColors == 2)
1358 } 1263 rowSize = (w + 7) / 8;
1359 } 1264 } else if (filter_id == rfb.TightFilterGradient) {
1360 } 1265 useGradient = true;
1361 } else { 1266 } else if (filter_id != rfb.TightFilterCopy) {
1362 // Data was compressed with zlib. 1267 throw new Exception("Incorrect tight filter id: " + filter_id);
1363 int zlibDataLen = rfb.readCompactLen(); 1268 }
1364 byte[] zlibData = new byte[zlibDataLen]; 1269 }
1365 rfb.readFully(zlibData); 1270 if (numColors == 0 && bytesPixel == 4)
1366 if (rfb.rec != null && rfb.recordFromBeginning) { 1271 rowSize *= 3;
1367 rfb.rec.write(zlibData); 1272
1368 } 1273 // Read, optionally uncompress and decode data.
1369 int stream_id = comp_ctl & 0x03; 1274 int dataSize = h * rowSize;
1370 if (tightInflaters[stream_id] == null) { 1275 if (dataSize < rfb.TightMinToCompress) {
1371 tightInflaters[stream_id] = new Inflater(); 1276 // Data size is small - not compressed with zlib.
1372 } 1277 if (numColors != 0) {
1373 Inflater myInflater = tightInflaters[stream_id]; 1278 // Indexed colors.
1374 myInflater.setInput(zlibData); 1279 byte[] indexedData = new byte[dataSize];
1375 byte[] buf = new byte[dataSize]; 1280 rfb.readFully(indexedData);
1376 myInflater.inflate(buf); 1281 if (rfb.rec != null) {
1377 if (rfb.rec != null && !rfb.recordFromBeginning) { 1282 rfb.rec.write(indexedData);
1378 rfb.recordCompressedData(buf); 1283 }
1379 } 1284 if (numColors == 2) {
1380 1285 // Two colors.
1381 if (numColors != 0) { 1286 if (bytesPixel == 1) {
1382 // Indexed colors. 1287 decodeMonoData(x, y, w, h, indexedData, palette8);
1383 if (numColors == 2) { 1288 } else {
1384 // Two colors. 1289 decodeMonoData(x, y, w, h, indexedData, palette24);
1290 }
1291 } else {
1292 // 3..255 colors (assuming bytesPixel == 4).
1293 int i = 0;
1294 for (int dy = y; dy < y + h; dy++) {
1295 for (int dx = x; dx < x + w; dx++) {
1296 pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
1297 }
1298 }
1299 }
1300 } else if (useGradient) {
1301 // "Gradient"-processed data
1302 byte[] buf = new byte[w * h * 3];
1303 rfb.readFully(buf);
1304 if (rfb.rec != null) {
1305 rfb.rec.write(buf);
1306 }
1307 decodeGradientData(x, y, w, h, buf);
1308 } else {
1309 // Raw truecolor data.
1385 if (bytesPixel == 1) { 1310 if (bytesPixel == 1) {
1386 decodeMonoData(x, y, w, h, buf, palette8); 1311 for (int dy = y; dy < y + h; dy++) {
1312 rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
1313 if (rfb.rec != null) {
1314 rfb.rec.write(pixels8, dy * rfb.framebufferWidth
1315 + x, w);
1316 }
1317 }
1387 } else { 1318 } else {
1388 decodeMonoData(x, y, w, h, buf, palette24); 1319 byte[] buf = new byte[w * 3];
1389 } 1320 int i, offset;
1321 for (int dy = y; dy < y + h; dy++) {
1322 rfb.readFully(buf);
1323 if (rfb.rec != null) {
1324 rfb.rec.write(buf);
1325 }
1326 offset = dy * rfb.framebufferWidth + x;
1327 for (i = 0; i < w; i++) {
1328 pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16
1329 | (buf[i * 3 + 1] & 0xFF) << 8
1330 | (buf[i * 3 + 2] & 0xFF);
1331 }
1332 }
1333 }
1334 }
1335 } else {
1336 // Data was compressed with zlib.
1337 int zlibDataLen = rfb.readCompactLen();
1338 byte[] zlibData = new byte[zlibDataLen];
1339 rfb.readFully(zlibData);
1340 if (rfb.rec != null && rfb.recordFromBeginning) {
1341 rfb.rec.write(zlibData);
1342 }
1343 int stream_id = comp_ctl & 0x03;
1344 if (tightInflaters[stream_id] == null) {
1345 tightInflaters[stream_id] = new Inflater();
1346 }
1347 Inflater myInflater = tightInflaters[stream_id];
1348 myInflater.setInput(zlibData);
1349 byte[] buf = new byte[dataSize];
1350 myInflater.inflate(buf);
1351 if (rfb.rec != null && !rfb.recordFromBeginning) {
1352 rfb.recordCompressedData(buf);
1353 }
1354
1355 if (numColors != 0) {
1356 // Indexed colors.
1357 if (numColors == 2) {
1358 // Two colors.
1359 if (bytesPixel == 1) {
1360 decodeMonoData(x, y, w, h, buf, palette8);
1361 } else {
1362 decodeMonoData(x, y, w, h, buf, palette24);
1363 }
1364 } else {
1365 // More than two colors (assuming bytesPixel == 4).
1366 int i = 0;
1367 for (int dy = y; dy < y + h; dy++) {
1368 for (int dx = x; dx < x + w; dx++) {
1369 pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
1370 }
1371 }
1372 }
1373 } else if (useGradient) {
1374 // Compressed "Gradient"-filtered data (assuming bytesPixel ==
1375 // 4).
1376 decodeGradientData(x, y, w, h, buf);
1390 } else { 1377 } else {
1391 // More than two colors (assuming bytesPixel == 4). 1378 // Compressed truecolor data.
1392 int i = 0; 1379 if (bytesPixel == 1) {
1393 for (int dy = y; dy < y + h; dy++) { 1380 int destOffset = y * rfb.framebufferWidth + x;
1394 for (int dx = x; dx < x + w; dx++) { 1381 for (int dy = 0; dy < h; dy++) {
1395 pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; 1382 System.arraycopy(buf, dy * w, pixels8, destOffset, w);
1396 } 1383 destOffset += rfb.framebufferWidth;
1397 } 1384 }
1398 } 1385 } else {
1399 } else if (useGradient) { 1386 int srcOffset = 0;
1400 // Compressed "Gradient"-filtered data (assuming bytesPixel == 1387 int destOffset, i;
1401 // 4). 1388 for (int dy = 0; dy < h; dy++) {
1402 decodeGradientData(x, y, w, h, buf); 1389 myInflater.inflate(buf);
1390 destOffset = (y + dy) * rfb.framebufferWidth + x;
1391 for (i = 0; i < w; i++) {
1392 pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16
1393 | (buf[srcOffset + 1] & 0xFF) << 8
1394 | (buf[srcOffset + 2] & 0xFF);
1395 srcOffset += 3;
1396 }
1397 }
1398 }
1399 }
1400 }
1401
1402 handleUpdatedPixels(x, y, w, h);
1403 scheduleRepaint(x, y, w, h);
1404 }
1405
1406 //
1407 // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
1408 //
1409
1410 void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) {
1411
1412 int dx, dy, n;
1413 int i = y * rfb.framebufferWidth + x;
1414 int rowBytes = (w + 7) / 8;
1415 byte b;
1416
1417 for (dy = 0; dy < h; dy++) {
1418 for (dx = 0; dx < w / 8; dx++) {
1419 b = src[dy * rowBytes + dx];
1420 for (n = 7; n >= 0; n--)
1421 pixels8[i++] = palette[b >> n & 1];
1422 }
1423 for (n = 7; n >= 8 - w % 8; n--) {
1424 pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
1425 }
1426 i += (rfb.framebufferWidth - w);
1427 }
1428 }
1429
1430 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
1431
1432 int dx, dy, n;
1433 int i = y * rfb.framebufferWidth + x;
1434 int rowBytes = (w + 7) / 8;
1435 byte b;
1436
1437 for (dy = 0; dy < h; dy++) {
1438 for (dx = 0; dx < w / 8; dx++) {
1439 b = src[dy * rowBytes + dx];
1440 for (n = 7; n >= 0; n--)
1441 pixels24[i++] = palette[b >> n & 1];
1442 }
1443 for (n = 7; n >= 8 - w % 8; n--) {
1444 pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
1445 }
1446 i += (rfb.framebufferWidth - w);
1447 }
1448 }
1449
1450 //
1451 // Decode data processed with the "Gradient" filter.
1452 //
1453
1454 void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
1455
1456 int dx, dy, c;
1457 byte[] prevRow = new byte[w * 3];
1458 byte[] thisRow = new byte[w * 3];
1459 byte[] pix = new byte[3];
1460 int[] est = new int[3];
1461
1462 int offset = y * rfb.framebufferWidth + x;
1463
1464 for (dy = 0; dy < h; dy++) {
1465
1466 /* First pixel in a row */
1467 for (c = 0; c < 3; c++) {
1468 pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
1469 thisRow[c] = pix[c];
1470 }
1471 pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
1472 | (pix[2] & 0xFF);
1473
1474 /* Remaining pixels of a row */
1475 for (dx = 1; dx < w; dx++) {
1476 for (c = 0; c < 3; c++) {
1477 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
1478 * 3 + c] & 0xFF));
1479 if (est[c] > 0xFF) {
1480 est[c] = 0xFF;
1481 } else if (est[c] < 0x00) {
1482 est[c] = 0x00;
1483 }
1484 pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
1485 thisRow[dx * 3 + c] = pix[c];
1486 }
1487 pixels24[offset++] = (pix[0] & 0xFF) << 16
1488 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
1489 }
1490
1491 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
1492 offset += (rfb.framebufferWidth - w);
1493 }
1494 }
1495
1496 //
1497 // Display newly updated area of pixels.
1498 //
1499
1500 void handleUpdatedPixels(int x, int y, int w, int h) {
1501
1502 // Draw updated pixels of the off-screen image.
1503 pixelsSource.newPixels(x, y, w, h);
1504 memGraphics.setClip(x, y, w, h);
1505 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
1506 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
1507 }
1508
1509 //
1510 // Tell JVM to repaint specified desktop area.
1511 //
1512
1513 void scheduleRepaint(int x, int y, int w, int h) {
1514 // Request repaint, deferred if necessary.
1515 if (rfb.framebufferWidth == scaledWidth) {
1516 repaint(viewer.deferScreenUpdates, x, y, w, h);
1403 } else { 1517 } else {
1404 // Compressed truecolor data. 1518 int sx = x * scalingFactor / 100;
1405 if (bytesPixel == 1) { 1519 int sy = y * scalingFactor / 100;
1406 int destOffset = y * rfb.framebufferWidth + x; 1520 int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1;
1407 for (int dy = 0; dy < h; dy++) { 1521 int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1;
1408 System.arraycopy(buf, dy * w, pixels8, destOffset, w); 1522 repaint(viewer.deferScreenUpdates, sx, sy, sw, sh);
1409 destOffset += rfb.framebufferWidth; 1523 }
1524 }
1525
1526 //
1527 // Handle events.
1528 //
1529
1530 public void keyPressed(KeyEvent evt) {
1531 processLocalKeyEvent(evt);
1532 }
1533
1534 public void keyReleased(KeyEvent evt) {
1535 processLocalKeyEvent(evt);
1536 }
1537
1538 public void keyTyped(KeyEvent evt) {
1539 evt.consume();
1540 }
1541
1542 public void mousePressed(MouseEvent evt) {
1543 processLocalMouseEvent(evt, false);
1544 }
1545
1546 public void mouseReleased(MouseEvent evt) {
1547 processLocalMouseEvent(evt, false);
1548 }
1549
1550 public void mouseMoved(MouseEvent evt) {
1551 processLocalMouseEvent(evt, true);
1552 }
1553
1554 public void mouseDragged(MouseEvent evt) {
1555 processLocalMouseEvent(evt, true);
1556 }
1557
1558 public void processLocalKeyEvent(KeyEvent evt) {
1559 if (viewer.rfb != null && rfb.inNormalProtocol) {
1560 if (!inputEnabled) {
1561 if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R')
1562 && evt.getID() == KeyEvent.KEY_PRESSED) {
1563 // Request screen update.
1564 try {
1565 rfb.writeFramebufferUpdateRequest(0, 0,
1566 rfb.framebufferWidth, rfb.framebufferHeight,
1567 false);
1568 } catch (IOException e) {
1569 e.printStackTrace();
1570 }
1410 } 1571 }
1411 } else { 1572 } else {
1412 int srcOffset = 0; 1573 // Input enabled.
1413 int destOffset, i; 1574 synchronized (rfb) {
1414 for (int dy = 0; dy < h; dy++) { 1575 try {
1415 myInflater.inflate(buf); 1576 rfb.writeKeyEvent(evt);
1416 destOffset = (y + dy) * rfb.framebufferWidth + x; 1577 } catch (Exception e) {
1417 for (i = 0; i < w; i++) { 1578 e.printStackTrace();
1418 pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 1579 }
1419 | (buf[srcOffset + 1] & 0xFF) << 8 1580 rfb.notify();
1420 | (buf[srcOffset + 2] & 0xFF); 1581 }
1421 srcOffset += 3; 1582 }
1422 } 1583 }
1423 } 1584 // Don't ever pass keyboard events to AWT for default processing.
1424 } 1585 // Otherwise, pressing Tab would switch focus to ButtonPanel etc.
1425 } 1586 evt.consume();
1426 } 1587 }
1427 1588
1428 handleUpdatedPixels(x, y, w, h); 1589 public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
1429 scheduleRepaint(x, y, w, h); 1590 if (viewer.rfb != null && rfb.inNormalProtocol) {
1430 } 1591 if (moved) {
1431 1592 softCursorMove(evt.getX(), evt.getY());
1432 // 1593 }
1433 // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions). 1594 if (rfb.framebufferWidth != scaledWidth) {
1434 // 1595 int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor;
1435 1596 int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor;
1436 void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) { 1597 evt.translatePoint(sx - evt.getX(), sy - evt.getY());
1437 1598 }
1438 int dx, dy, n;
1439 int i = y * rfb.framebufferWidth + x;
1440 int rowBytes = (w + 7) / 8;
1441 byte b;
1442
1443 for (dy = 0; dy < h; dy++) {
1444 for (dx = 0; dx < w / 8; dx++) {
1445 b = src[dy * rowBytes + dx];
1446 for (n = 7; n >= 0; n--)
1447 pixels8[i++] = palette[b >> n & 1];
1448 }
1449 for (n = 7; n >= 8 - w % 8; n--) {
1450 pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
1451 }
1452 i += (rfb.framebufferWidth - w);
1453 }
1454 }
1455
1456 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
1457
1458 int dx, dy, n;
1459 int i = y * rfb.framebufferWidth + x;
1460 int rowBytes = (w + 7) / 8;
1461 byte b;
1462
1463 for (dy = 0; dy < h; dy++) {
1464 for (dx = 0; dx < w / 8; dx++) {
1465 b = src[dy * rowBytes + dx];
1466 for (n = 7; n >= 0; n--)
1467 pixels24[i++] = palette[b >> n & 1];
1468 }
1469 for (n = 7; n >= 8 - w % 8; n--) {
1470 pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
1471 }
1472 i += (rfb.framebufferWidth - w);
1473 }
1474 }
1475
1476 //
1477 // Decode data processed with the "Gradient" filter.
1478 //
1479
1480 void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
1481
1482 int dx, dy, c;
1483 byte[] prevRow = new byte[w * 3];
1484 byte[] thisRow = new byte[w * 3];
1485 byte[] pix = new byte[3];
1486 int[] est = new int[3];
1487
1488 int offset = y * rfb.framebufferWidth + x;
1489
1490 for (dy = 0; dy < h; dy++) {
1491
1492 /* First pixel in a row */
1493 for (c = 0; c < 3; c++) {
1494 pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]);
1495 thisRow[c] = pix[c];
1496 }
1497 pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8
1498 | (pix[2] & 0xFF);
1499
1500 /* Remaining pixels of a row */
1501 for (dx = 1; dx < w; dx++) {
1502 for (c = 0; c < 3; c++) {
1503 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1)
1504 * 3 + c] & 0xFF));
1505 if (est[c] > 0xFF) {
1506 est[c] = 0xFF;
1507 } else if (est[c] < 0x00) {
1508 est[c] = 0x00;
1509 }
1510 pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]);
1511 thisRow[dx * 3 + c] = pix[c];
1512 }
1513 pixels24[offset++] = (pix[0] & 0xFF) << 16
1514 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
1515 }
1516
1517 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
1518 offset += (rfb.framebufferWidth - w);
1519 }
1520 }
1521
1522 //
1523 // Display newly updated area of pixels.
1524 //
1525
1526 void handleUpdatedPixels(int x, int y, int w, int h) {
1527
1528 // Draw updated pixels of the off-screen image.
1529 pixelsSource.newPixels(x, y, w, h);
1530 memGraphics.setClip(x, y, w, h);
1531 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
1532 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
1533 }
1534
1535 //
1536 // Tell JVM to repaint specified desktop area.
1537 //
1538
1539 void scheduleRepaint(int x, int y, int w, int h) {
1540 // Request repaint, deferred if necessary.
1541 if (rfb.framebufferWidth == scaledWidth) {
1542 repaint(viewer.deferScreenUpdates, x, y, w, h);
1543 } else {
1544 int sx = x * scalingFactor / 100;
1545 int sy = y * scalingFactor / 100;
1546 int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1;
1547 int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1;
1548 repaint(viewer.deferScreenUpdates, sx, sy, sw, sh);
1549 }
1550 }
1551
1552 //
1553 // Handle events.
1554 //
1555
1556 public void keyPressed(KeyEvent evt) {
1557 processLocalKeyEvent(evt);
1558 }
1559
1560 public void keyReleased(KeyEvent evt) {
1561 processLocalKeyEvent(evt);
1562 }
1563
1564 public void keyTyped(KeyEvent evt) {
1565 evt.consume();
1566 }
1567
1568 public void mousePressed(MouseEvent evt) {
1569 processLocalMouseEvent(evt, false);
1570 }
1571
1572 public void mouseReleased(MouseEvent evt) {
1573 processLocalMouseEvent(evt, false);
1574 }
1575
1576 public void mouseMoved(MouseEvent evt) {
1577 processLocalMouseEvent(evt, true);
1578 }
1579
1580 public void mouseDragged(MouseEvent evt) {
1581 processLocalMouseEvent(evt, true);
1582 }
1583
1584 public void processLocalKeyEvent(KeyEvent evt) {
1585 if (viewer.rfb != null && rfb.inNormalProtocol) {
1586 if (!inputEnabled) {
1587 if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R')
1588 && evt.getID() == KeyEvent.KEY_PRESSED) {
1589 // Request screen update.
1590 try {
1591 rfb.writeFramebufferUpdateRequest(0, 0,
1592 rfb.framebufferWidth, rfb.framebufferHeight,
1593 false);
1594 } catch (IOException e) {
1595 e.printStackTrace();
1596 }
1597 }
1598 } else {
1599 // Input enabled.
1600 synchronized (rfb) { 1599 synchronized (rfb) {
1601 try { 1600 try {
1602 rfb.writeKeyEvent(evt); 1601 rfb.writePointerEvent(evt);
1603 } catch (Exception e) { 1602 } catch (Exception e) {
1604 e.printStackTrace(); 1603 e.printStackTrace();
1605 } 1604 }
1606 rfb.notify(); 1605 rfb.notify();
1607 } 1606 }
1608 } 1607 }
1609 } 1608 }
1610 // Don't ever pass keyboard events to AWT for default processing. 1609
1611 // Otherwise, pressing Tab would switch focus to ButtonPanel etc. 1610 //
1612 evt.consume(); 1611 // Ignored events.
1613 } 1612 //
1614 1613
1615 public void processLocalMouseEvent(MouseEvent evt, boolean moved) { 1614 public void mouseClicked(MouseEvent evt) {
1616 if (viewer.rfb != null && rfb.inNormalProtocol) { 1615 }
1617 if (moved) { 1616
1618 softCursorMove(evt.getX(), evt.getY()); 1617 public void mouseEntered(MouseEvent evt) {
1619 } 1618 }
1620 if (rfb.framebufferWidth != scaledWidth) { 1619
1621 int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor; 1620 public void mouseExited(MouseEvent evt) {
1622 int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor; 1621 }
1623 evt.translatePoint(sx - evt.getX(), sy - evt.getY()); 1622
1624 } 1623 //
1625 synchronized (rfb) { 1624 // Reset update statistics.
1626 try { 1625 //
1627 rfb.writePointerEvent(evt); 1626
1628 } catch (Exception e) { 1627 void resetStats() {
1629 e.printStackTrace(); 1628 statStartTime = System.currentTimeMillis();
1630 } 1629 statNumUpdates = 0;
1631 rfb.notify(); 1630 statNumTotalRects = 0;
1632 } 1631 statNumPixelRects = 0;
1633 } 1632 statNumRectsTight = 0;
1634 } 1633 statNumRectsTightJPEG = 0;
1635 1634 statNumRectsZRLE = 0;
1636 // 1635 statNumRectsHextile = 0;
1637 // Ignored events. 1636 statNumRectsRaw = 0;
1638 // 1637 statNumRectsCopy = 0;
1639 1638 statNumBytesEncoded = 0;
1640 public void mouseClicked(MouseEvent evt) { 1639 statNumBytesDecoded = 0;
1641 } 1640 }
1642 1641
1643 public void mouseEntered(MouseEvent evt) { 1642 // ////////////////////////////////////////////////////////////////
1644 } 1643 //
1645 1644 // Handle cursor shape updates (XCursor and RichCursor encodings).
1646 public void mouseExited(MouseEvent evt) { 1645 //
1647 } 1646
1648 1647 boolean showSoftCursor = false;
1649 // 1648
1650 // Reset update statistics. 1649 MemoryImageSource softCursorSource;
1651 // 1650 Image softCursor;
1652 1651
1653 void resetStats() { 1652 int cursorX = 0, cursorY = 0;
1654 statStartTime = System.currentTimeMillis(); 1653 int cursorWidth, cursorHeight;
1655 statNumUpdates = 0; 1654 int origCursorWidth, origCursorHeight;
1656 statNumTotalRects = 0; 1655 int hotX, hotY;
1657 statNumPixelRects = 0; 1656 int origHotX, origHotY;
1658 statNumRectsTight = 0; 1657
1659 statNumRectsTightJPEG = 0; 1658 //
1660 statNumRectsZRLE = 0; 1659 // Handle cursor shape update (XCursor and RichCursor encodings).
1661 statNumRectsHextile = 0; 1660 //
1662 statNumRectsRaw = 0; 1661
1663 statNumRectsCopy = 0; 1662 synchronized void handleCursorShapeUpdate(int encodingType, int xhot,
1664 statNumBytesEncoded = 0; 1663 int yhot, int width, int height) throws IOException {
1665 statNumBytesDecoded = 0; 1664
1666 } 1665 softCursorFree();
1667 1666
1668 // //////////////////////////////////////////////////////////////// 1667 if (width * height == 0)
1669 // 1668 return;
1670 // Handle cursor shape updates (XCursor and RichCursor encodings). 1669
1671 // 1670 // Ignore cursor shape data if requested by user.
1672 1671 if (viewer.options.ignoreCursorUpdates) {
1673 boolean showSoftCursor = false; 1672 int bytesPerRow = (width + 7) / 8;
1674 1673 int bytesMaskData = bytesPerRow * height;
1675 MemoryImageSource softCursorSource; 1674
1676 Image softCursor; 1675 if (encodingType == rfb.EncodingXCursor) {
1677 1676 rfb.skipBytes(6 + bytesMaskData * 2);
1678 int cursorX = 0, cursorY = 0; 1677 } else {
1679 int cursorWidth, cursorHeight; 1678 // rfb.EncodingRichCursor
1680 int origCursorWidth, origCursorHeight; 1679 rfb.skipBytes(width * height * bytesPixel + bytesMaskData);
1681 int hotX, hotY; 1680 }
1682 int origHotX, origHotY; 1681 return;
1683 1682 }
1684 // 1683
1685 // Handle cursor shape update (XCursor and RichCursor encodings). 1684 // Decode cursor pixel data.
1686 // 1685 softCursorSource = decodeCursorShape(encodingType, width, height);
1687 1686
1688 synchronized void handleCursorShapeUpdate(int encodingType, int xhot, 1687 // Set original (non-scaled) cursor dimensions.
1689 int yhot, int width, int height) throws IOException { 1688 origCursorWidth = width;
1690 1689 origCursorHeight = height;
1691 softCursorFree(); 1690 origHotX = xhot;
1692 1691 origHotY = yhot;
1693 if (width * height == 0) 1692
1694 return; 1693 // Create off-screen cursor image.
1695 1694 createSoftCursor();
1696 // Ignore cursor shape data if requested by user. 1695
1697 if (viewer.options.ignoreCursorUpdates) { 1696 // Show the cursor.
1697 showSoftCursor = true;
1698 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
1699 cursorWidth, cursorHeight);
1700 }
1701
1702 //
1703 // decodeCursorShape(). Decode cursor pixel data and return
1704 // corresponding MemoryImageSource instance.
1705 //
1706
1707 synchronized MemoryImageSource decodeCursorShape(int encodingType,
1708 int width, int height) throws IOException {
1709
1698 int bytesPerRow = (width + 7) / 8; 1710 int bytesPerRow = (width + 7) / 8;
1699 int bytesMaskData = bytesPerRow * height; 1711 int bytesMaskData = bytesPerRow * height;
1700 1712
1713 int[] softCursorPixels = new int[width * height];
1714
1701 if (encodingType == rfb.EncodingXCursor) { 1715 if (encodingType == rfb.EncodingXCursor) {
1702 rfb.skipBytes(6 + bytesMaskData * 2); 1716
1703 } else { 1717 // Read foreground and background colors of the cursor.
1704 // rfb.EncodingRichCursor 1718 byte[] rgb = new byte[6];
1705 rfb.skipBytes(width * height * bytesPixel + bytesMaskData); 1719 rfb.readFully(rgb);
1706 } 1720 int[] colors = {
1707 return; 1721 (0xFF000000 | (rgb[3] & 0xFF) << 16 | (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)),
1708 } 1722 (0xFF000000 | (rgb[0] & 0xFF) << 16 | (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) };
1709 1723
1710 // Decode cursor pixel data. 1724 // Read pixel and mask data.
1711 softCursorSource = decodeCursorShape(encodingType, width, height); 1725 byte[] pixBuf = new byte[bytesMaskData];
1712 1726 rfb.readFully(pixBuf);
1713 // Set original (non-scaled) cursor dimensions. 1727 byte[] maskBuf = new byte[bytesMaskData];
1714 origCursorWidth = width; 1728 rfb.readFully(maskBuf);
1715 origCursorHeight = height; 1729
1716 origHotX = xhot; 1730 // Decode pixel data into softCursorPixels[].
1717 origHotY = yhot; 1731 byte pixByte, maskByte;
1718 1732 int x, y, n, result;
1719 // Create off-screen cursor image. 1733 int i = 0;
1720 createSoftCursor(); 1734 for (y = 0; y < height; y++) {
1721 1735 for (x = 0; x < width / 8; x++) {
1722 // Show the cursor. 1736 pixByte = pixBuf[y * bytesPerRow + x];
1723 showSoftCursor = true; 1737 maskByte = maskBuf[y * bytesPerRow + x];
1724 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, 1738 for (n = 7; n >= 0; n--) {
1725 cursorWidth, cursorHeight); 1739 if ((maskByte >> n & 1) != 0) {
1726 } 1740 result = colors[pixByte >> n & 1];
1727 1741 } else {
1728 // 1742 result = 0; // Transparent pixel
1729 // decodeCursorShape(). Decode cursor pixel data and return 1743 }
1730 // corresponding MemoryImageSource instance. 1744 softCursorPixels[i++] = result;
1731 // 1745 }
1732 1746 }
1733 synchronized MemoryImageSource decodeCursorShape(int encodingType, 1747 for (n = 7; n >= 8 - width % 8; n--) {
1734 int width, int height) throws IOException { 1748 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
1735 1749 result = colors[pixBuf[y * bytesPerRow + x] >> n & 1];
1736 int bytesPerRow = (width + 7) / 8;
1737 int bytesMaskData = bytesPerRow * height;
1738
1739 int[] softCursorPixels = new int[width * height];
1740
1741 if (encodingType == rfb.EncodingXCursor) {
1742
1743 // Read foreground and background colors of the cursor.
1744 byte[] rgb = new byte[6];
1745 rfb.readFully(rgb);
1746 int[] colors = {
1747 (0xFF000000 | (rgb[3] & 0xFF) << 16 | (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)),
1748 (0xFF000000 | (rgb[0] & 0xFF) << 16 | (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) };
1749
1750 // Read pixel and mask data.
1751 byte[] pixBuf = new byte[bytesMaskData];
1752 rfb.readFully(pixBuf);
1753 byte[] maskBuf = new byte[bytesMaskData];
1754 rfb.readFully(maskBuf);
1755
1756 // Decode pixel data into softCursorPixels[].
1757 byte pixByte, maskByte;
1758 int x, y, n, result;
1759 int i = 0;
1760 for (y = 0; y < height; y++) {
1761 for (x = 0; x < width / 8; x++) {
1762 pixByte = pixBuf[y * bytesPerRow + x];
1763 maskByte = maskBuf[y * bytesPerRow + x];
1764 for (n = 7; n >= 0; n--) {
1765 if ((maskByte >> n & 1) != 0) {
1766 result = colors[pixByte >> n & 1];
1767 } else { 1750 } else {
1768 result = 0; // Transparent pixel 1751 result = 0; // Transparent pixel
1769 } 1752 }
1770 softCursorPixels[i++] = result; 1753 softCursorPixels[i++] = result;
1771 } 1754 }
1772 } 1755 }
1773 for (n = 7; n >= 8 - width % 8; n--) { 1756
1774 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { 1757 } else {
1775 result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; 1758 // encodingType == rfb.EncodingRichCursor
1776 } else { 1759
1777 result = 0; // Transparent pixel 1760 // Read pixel and mask data.
1778 } 1761 byte[] pixBuf = new byte[width * height * bytesPixel];
1779 softCursorPixels[i++] = result; 1762 rfb.readFully(pixBuf);
1780 } 1763 byte[] maskBuf = new byte[bytesMaskData];
1781 } 1764 rfb.readFully(maskBuf);
1782 1765
1783 } else { 1766 // Decode pixel data into softCursorPixels[].
1784 // encodingType == rfb.EncodingRichCursor 1767 byte pixByte, maskByte;
1785 1768 int x, y, n, result;
1786 // Read pixel and mask data. 1769 int i = 0;
1787 byte[] pixBuf = new byte[width * height * bytesPixel]; 1770 for (y = 0; y < height; y++) {
1788 rfb.readFully(pixBuf); 1771 for (x = 0; x < width / 8; x++) {
1789 byte[] maskBuf = new byte[bytesMaskData]; 1772 maskByte = maskBuf[y * bytesPerRow + x];
1790 rfb.readFully(maskBuf); 1773 for (n = 7; n >= 0; n--) {
1791 1774 if ((maskByte >> n & 1) != 0) {
1792 // Decode pixel data into softCursorPixels[]. 1775 if (bytesPixel == 1) {
1793 byte pixByte, maskByte; 1776 result = cm8.getRGB(pixBuf[i]);
1794 int x, y, n, result; 1777 } else {
1795 int i = 0; 1778 result = 0xFF000000
1796 for (y = 0; y < height; y++) { 1779 | (pixBuf[i * 4 + 2] & 0xFF) << 16
1797 for (x = 0; x < width / 8; x++) { 1780 | (pixBuf[i * 4 + 1] & 0xFF) << 8
1798 maskByte = maskBuf[y * bytesPerRow + x]; 1781 | (pixBuf[i * 4] & 0xFF);
1799 for (n = 7; n >= 0; n--) { 1782 }
1800 if ((maskByte >> n & 1) != 0) { 1783 } else {
1784 result = 0; // Transparent pixel
1785 }
1786 softCursorPixels[i++] = result;
1787 }
1788 }
1789 for (n = 7; n >= 8 - width % 8; n--) {
1790 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
1801 if (bytesPixel == 1) { 1791 if (bytesPixel == 1) {
1802 result = cm8.getRGB(pixBuf[i]); 1792 result = cm8.getRGB(pixBuf[i]);
1803 } else { 1793 } else {
1804 result = 0xFF000000 1794 result = 0xFF000000
1805 | (pixBuf[i * 4 + 2] & 0xFF) << 16 1795 | (pixBuf[i * 4 + 2] & 0xFF) << 16
1810 result = 0; // Transparent pixel 1800 result = 0; // Transparent pixel
1811 } 1801 }
1812 softCursorPixels[i++] = result; 1802 softCursorPixels[i++] = result;
1813 } 1803 }
1814 } 1804 }
1815 for (n = 7; n >= 8 - width % 8; n--) { 1805
1816 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { 1806 }
1817 if (bytesPixel == 1) { 1807
1818 result = cm8.getRGB(pixBuf[i]); 1808 return new MemoryImageSource(width, height, softCursorPixels, 0, width);
1819 } else { 1809 }
1820 result = 0xFF000000 1810
1821 | (pixBuf[i * 4 + 2] & 0xFF) << 16 1811 //
1822 | (pixBuf[i * 4 + 1] & 0xFF) << 8 1812 // createSoftCursor(). Assign softCursor new Image (scaled if necessary).
1823 | (pixBuf[i * 4] & 0xFF); 1813 // Uses softCursorSource as a source for new cursor image.
1824 } 1814 //
1825 } else { 1815
1826 result = 0; // Transparent pixel 1816 synchronized void createSoftCursor() {
1827 } 1817
1828 softCursorPixels[i++] = result; 1818 if (softCursorSource == null)
1829 } 1819 return;
1830 } 1820
1831 1821 int scaleCursor = viewer.options.scaleCursor;
1832 } 1822 if (scaleCursor == 0 || !inputEnabled)
1833 1823 scaleCursor = 100;
1834 return new MemoryImageSource(width, height, softCursorPixels, 0, width); 1824
1825 // Save original cursor coordinates.
1826 int x = cursorX - hotX;
1827 int y = cursorY - hotY;
1828 int w = cursorWidth;
1829 int h = cursorHeight;
1830
1831 cursorWidth = (origCursorWidth * scaleCursor + 50) / 100;
1832 cursorHeight = (origCursorHeight * scaleCursor + 50) / 100;
1833 hotX = (origHotX * scaleCursor + 50) / 100;
1834 hotY = (origHotY * scaleCursor + 50) / 100;
1835 softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource);
1836
1837 if (scaleCursor != 100) {
1838 softCursor = softCursor.getScaledInstance(cursorWidth,
1839 cursorHeight, Image.SCALE_SMOOTH);
1840 }
1841
1842 if (showSoftCursor) {
1843 // Compute screen area to update.
1844 x = Math.min(x, cursorX - hotX);
1845 y = Math.min(y, cursorY - hotY);
1846 w = Math.max(w, cursorWidth);
1847 h = Math.max(h, cursorHeight);
1848
1849 repaint(viewer.deferCursorUpdates, x, y, w, h);
1850 }
1851 }
1852
1853 //
1854 // softCursorMove(). Moves soft cursor into a particular location.
1855 //
1856
1857 synchronized void softCursorMove(int x, int y) {
1858 int oldX = cursorX;
1859 int oldY = cursorY;
1860 cursorX = x;
1861 cursorY = y;
1862 if (showSoftCursor) {
1863 repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY,
1864 cursorWidth, cursorHeight);
1865 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
1866 cursorWidth, cursorHeight);
1867 }
1868 }
1869
1870 //
1871 // softCursorFree(). Remove soft cursor, dispose resources.
1872 //
1873
1874 synchronized void softCursorFree() {
1875 if (showSoftCursor) {
1876 showSoftCursor = false;
1877 softCursor = null;
1878 softCursorSource = null;
1879
1880 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
1881 cursorWidth, cursorHeight);
1882 }
1883 }
1835 } 1884 }
1836
1837 //
1838 // createSoftCursor(). Assign softCursor new Image (scaled if necessary).
1839 // Uses softCursorSource as a source for new cursor image.
1840 //
1841
1842 synchronized void createSoftCursor() {
1843
1844 if (softCursorSource == null)
1845 return;
1846
1847 int scaleCursor = viewer.options.scaleCursor;
1848 if (scaleCursor == 0 || !inputEnabled)
1849 scaleCursor = 100;
1850
1851 // Save original cursor coordinates.
1852 int x = cursorX - hotX;
1853 int y = cursorY - hotY;
1854 int w = cursorWidth;
1855 int h = cursorHeight;
1856
1857 cursorWidth = (origCursorWidth * scaleCursor + 50) / 100;
1858 cursorHeight = (origCursorHeight * scaleCursor + 50) / 100;
1859 hotX = (origHotX * scaleCursor + 50) / 100;
1860 hotY = (origHotY * scaleCursor + 50) / 100;
1861 softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource);
1862
1863 if (scaleCursor != 100) {
1864 softCursor = softCursor.getScaledInstance(cursorWidth,
1865 cursorHeight, Image.SCALE_SMOOTH);
1866 }
1867
1868 if (showSoftCursor) {
1869 // Compute screen area to update.
1870 x = Math.min(x, cursorX - hotX);
1871 y = Math.min(y, cursorY - hotY);
1872 w = Math.max(w, cursorWidth);
1873 h = Math.max(h, cursorHeight);
1874
1875 repaint(viewer.deferCursorUpdates, x, y, w, h);
1876 }
1877 }
1878
1879 //
1880 // softCursorMove(). Moves soft cursor into a particular location.
1881 //
1882
1883 synchronized void softCursorMove(int x, int y) {
1884 int oldX = cursorX;
1885 int oldY = cursorY;
1886 cursorX = x;
1887 cursorY = y;
1888 if (showSoftCursor) {
1889 repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY,
1890 cursorWidth, cursorHeight);
1891 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
1892 cursorWidth, cursorHeight);
1893 }
1894 }
1895
1896 //
1897 // softCursorFree(). Remove soft cursor, dispose resources.
1898 //
1899
1900 synchronized void softCursorFree() {
1901 if (showSoftCursor) {
1902 showSoftCursor = false;
1903 softCursor = null;
1904 softCursorSource = null;
1905
1906 repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY,
1907 cursorWidth, cursorHeight);
1908 }
1909 }
1910 }