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