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