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