blob: 63325324d5fd8bda8e86141a9185f60f246b5b75 [file] [log] [blame]
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001//
2// Copyright (C) 2001,2002 HorizonLive.com, Inc. All Rights Reserved.
3// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
4// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
5// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
6//
7// This is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This software is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this software; if not, write to the Free Software
19// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20// USA.
21//
22
23import java.awt.*;
24import java.awt.event.*;
25import java.awt.image.*;
26import java.io.*;
27import java.lang.*;
28import java.util.zip.*;
29
30
31//
32// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
33//
34
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000035class VncCanvas extends Canvas {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000036
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000037 RfbPlayer player;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000038 RfbProto rfb;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000039 ColorModel cm24;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000040
41 Image memImage;
42 Graphics memGraphics;
43
44 Image rawPixelsImage;
45 MemoryImageSource pixelsSource;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000046 int[] pixels24;
47
48 // Zlib encoder's data.
49 byte[] zlibBuf;
50 int zlibBufLen = 0;
51 Inflater zlibInflater;
52
53 // Tight encoder's data.
54 final static int tightZlibBufferSize = 512;
55 Inflater[] tightInflaters;
56
57 // Since JPEG images are loaded asynchronously, we have to remember
58 // their position in the framebuffer. Also, this jpegRect object is
59 // used for synchronization between the rfbThread and a JVM's thread
60 // which decodes and loads JPEG images.
61 Rectangle jpegRect;
62
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000063 // When we're in the seeking mode, we should not update the desktop.
64 // This variable helps us to remember that repainting the desktop at
65 // once is necessary when the seek operation is finished.
66 boolean seekMode;
67
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000068 //
69 // The constructor.
70 //
71
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000072 VncCanvas(RfbPlayer player) throws IOException {
73 this.player = player;
74 rfb = player.rfb;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000075 seekMode = false;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000076
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000077 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
78
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000079 updateFramebufferSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000080 }
81
82 //
83 // Callback methods to determine geometry of our Component.
84 //
85
86 public Dimension getPreferredSize() {
87 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
88 }
89
90 public Dimension getMinimumSize() {
91 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
92 }
93
94 public Dimension getMaximumSize() {
95 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
96 }
97
98 //
99 // All painting is performed here.
100 //
101
102 public void update(Graphics g) {
103 paint(g);
104 }
105
106 public void paint(Graphics g) {
107 synchronized(memImage) {
108 g.drawImage(memImage, 0, 0, null);
109 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000110 }
111
112 //
113 // Override the ImageObserver interface method to handle drawing of
114 // JPEG-encoded data.
115 //
116
117 public boolean imageUpdate(Image img, int infoflags,
118 int x, int y, int width, int height) {
119 if ((infoflags & (ALLBITS | ABORT)) == 0) {
120 return true; // We need more image data.
121 } else {
122 // If the whole image is available, draw it now.
123 if ((infoflags & ALLBITS) != 0) {
124 if (jpegRect != null) {
125 synchronized(jpegRect) {
126 memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null);
127 scheduleRepaint(jpegRect.x, jpegRect.y,
128 jpegRect.width, jpegRect.height);
129 jpegRect.notify();
130 }
131 }
132 }
133 return false; // All image data was processed.
134 }
135 }
136
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000137 void updateFramebufferSize() {
138
139 // Useful shortcuts.
140 int fbWidth = rfb.framebufferWidth;
141 int fbHeight = rfb.framebufferHeight;
142
143 // Create new off-screen image either if it does not exist, or if
144 // its geometry should be changed. It's not necessary to replace
145 // existing image if only pixel format should be changed.
146 if (memImage == null) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000147 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000148 memGraphics = memImage.getGraphics();
149 } else if (memImage.getWidth(null) != fbWidth ||
150 memImage.getHeight(null) != fbHeight) {
151 synchronized(memImage) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000152 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000153 memGraphics = memImage.getGraphics();
154 }
155 }
156
157 // Images with raw pixels should be re-allocated on every change
158 // of geometry or pixel format.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000159 pixels24 = new int[fbWidth * fbHeight];
160 pixelsSource =
161 new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000162 pixelsSource.setAnimated(true);
163 rawPixelsImage = createImage(pixelsSource);
164
165 // Update the size of desktop containers.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000166 if (player.inSeparateFrame) {
167 if (player.desktopScrollPane != null)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000168 resizeDesktopFrame();
169 } else {
170 setSize(fbWidth, fbHeight);
171 }
172 }
173
174 void resizeDesktopFrame() {
175 setSize(rfb.framebufferWidth, rfb.framebufferHeight);
176
177 // FIXME: Find a better way to determine correct size of a
178 // ScrollPane. -- const
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000179 Insets insets = player.desktopScrollPane.getInsets();
180 player.desktopScrollPane.setSize(rfb.framebufferWidth +
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000181 2 * Math.min(insets.left, insets.right),
182 rfb.framebufferHeight +
183 2 * Math.min(insets.top, insets.bottom));
184
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000185 player.vncFrame.pack();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000186
187 // Try to limit the frame size to the screen size.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000188 Dimension screenSize = player.vncFrame.getToolkit().getScreenSize();
189 Dimension frameSize = player.vncFrame.getSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000190 Dimension newSize = frameSize;
191 boolean needToResizeFrame = false;
192 if (frameSize.height > screenSize.height) {
193 newSize.height = screenSize.height;
194 needToResizeFrame = true;
195 }
196 if (frameSize.width > screenSize.width) {
197 newSize.width = screenSize.width;
198 needToResizeFrame = true;
199 }
200 if (needToResizeFrame) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000201 player.vncFrame.setSize(newSize);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000202 }
203
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000204 player.desktopScrollPane.doLayout();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000205 }
206
207 //
208 // processNormalProtocol() - executed by the rfbThread to deal with the
209 // RFB socket.
210 //
211
212 public void processNormalProtocol() throws IOException {
213
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000214 zlibInflater = new Inflater();
215 tightInflaters = new Inflater[4];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000216
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000217 player.updatePos();
218
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000219 // Main dispatch loop.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000220
221 while (true) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000222
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000223 int msgType = rfb.readServerMessageType();
224
225 switch (msgType) {
226 case RfbProto.FramebufferUpdate:
227 rfb.readFramebufferUpdate();
228
229 for (int i = 0; i < rfb.updateNRects; i++) {
230 rfb.readFramebufferUpdateRectHdr();
231
232 if (rfb.updateRectEncoding == rfb.EncodingLastRect)
233 break;
234
235 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
236 rfb.setFramebufferSize(rfb.updateRectW, rfb.updateRectH);
237 updateFramebufferSize();
238 break;
239 }
240
241 if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
242 rfb.updateRectEncoding == rfb.EncodingRichCursor) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000243 throw new IOException("Sorry, no support for" +
244 " cursor shape updates yet");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000245 }
246
247 switch (rfb.updateRectEncoding) {
248
249 case RfbProto.EncodingRaw:
250 {
251 handleRawRect(rfb.updateRectX, rfb.updateRectY,
252 rfb.updateRectW, rfb.updateRectH);
253 break;
254 }
255
256 case RfbProto.EncodingCopyRect:
257 {
258 rfb.readCopyRect();
259
260 int sx = rfb.copyRectSrcX, sy = rfb.copyRectSrcY;
261 int rx = rfb.updateRectX, ry = rfb.updateRectY;
262 int rw = rfb.updateRectW, rh = rfb.updateRectH;
263
264 memGraphics.copyArea(sx, sy, rw, rh, rx - sx, ry - sy);
265
266 scheduleRepaint(rx, ry, rw, rh);
267 break;
268 }
269
270 case RfbProto.EncodingRRE:
271 {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000272 byte[] buf = new byte[4];
273
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000274 int rx = rfb.updateRectX, ry = rfb.updateRectY;
275 int rw = rfb.updateRectW, rh = rfb.updateRectH;
276 int nSubrects = rfb.is.readInt();
277 int x, y, w, h;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000278
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000279 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000280 Color pixel = new Color(buf[2] & 0xFF,
281 buf[1] & 0xFF,
282 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000283 memGraphics.setColor(pixel);
284 memGraphics.fillRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000285
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000286 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000287 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000288 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000289 x = rx + rfb.is.readUnsignedShort();
290 y = ry + rfb.is.readUnsignedShort();
291 w = rfb.is.readUnsignedShort();
292 h = rfb.is.readUnsignedShort();
293
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000294 memGraphics.setColor(pixel);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000295 memGraphics.fillRect(x, y, w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000296 }
297
298 scheduleRepaint(rx, ry, rw, rh);
299 break;
300 }
301
302 case RfbProto.EncodingCoRRE:
303 {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000304 byte[] buf = new byte[4];
305
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000306 int rx = rfb.updateRectX, ry = rfb.updateRectY;
307 int rw = rfb.updateRectW, rh = rfb.updateRectH;
308 int nSubrects = rfb.is.readInt();
309 int x, y, w, h;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000310
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000311 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000312 Color pixel = new Color(buf[2] & 0xFF,
313 buf[1] & 0xFF,
314 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000315 memGraphics.setColor(pixel);
316 memGraphics.fillRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000317
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000318 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000319 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000320 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000321 x = rx + rfb.is.readUnsignedByte();
322 y = ry + rfb.is.readUnsignedByte();
323 w = rfb.is.readUnsignedByte();
324 h = rfb.is.readUnsignedByte();
325
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000326 memGraphics.setColor(pixel);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000327 memGraphics.fillRect(x, y, w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000328 }
329
330 scheduleRepaint(rx, ry, rw, rh);
331 break;
332 }
333
334 case RfbProto.EncodingHextile:
335 {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000336 byte[] buf = new byte[256 * 4];
337
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000338 int rx = rfb.updateRectX, ry = rfb.updateRectY;
339 int rw = rfb.updateRectW, rh = rfb.updateRectH;
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000340 Color bg = new Color(0, 0, 0), fg = new Color(0, 0, 0);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000341
342 for (int ty = ry; ty < ry + rh; ty += 16) {
343
344 int th = 16;
345 if (ry + rh - ty < 16)
346 th = ry + rh - ty;
347
348 for (int tx = rx; tx < rx + rw; tx += 16) {
349
350 int tw = 16;
351 if (rx + rw - tx < 16)
352 tw = rx + rw - tx;
353
354 int subencoding = rfb.is.readUnsignedByte();
355
356 // Is it a raw-encoded sub-rectangle?
357 if ((subencoding & rfb.HextileRaw) != 0) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000358 int count, offset;
359 for (int j = ty; j < ty + th; j++) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000360 rfb.is.readFully(buf, 0, tw * 4);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000361 offset = j * rfb.framebufferWidth + tx;
362 for (count = 0; count < tw; count++) {
363 pixels24[offset + count] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000364 (buf[count * 4 + 2] & 0xFF) << 16 |
365 (buf[count * 4 + 1] & 0xFF) << 8 |
366 (buf[count * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000367 }
368 }
369 handleUpdatedPixels(tx, ty, tw, th);
370 continue;
371 }
372
373 // Read and draw the background if specified.
374 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000375 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000376 bg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000377 }
378 memGraphics.setColor(bg);
379 memGraphics.fillRect(tx, ty, tw, th);
380
381 // Read the foreground color if specified.
382 if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000383 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000384 fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000385 }
386
387 // Done with this tile if there is no sub-rectangles.
388 if ((subencoding & rfb.HextileAnySubrects) == 0)
389 continue;
390
391 int nSubrects = rfb.is.readUnsignedByte();
392
393 int b1, b2, sx, sy, sw, sh;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000394 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
395 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000396 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000397 fg = new Color(buf[2] & 0xFF,
398 buf[1] & 0xFF,
399 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000400 b1 = rfb.is.readUnsignedByte();
401 b2 = rfb.is.readUnsignedByte();
402 sx = tx + (b1 >> 4);
403 sy = ty + (b1 & 0xf);
404 sw = (b2 >> 4) + 1;
405 sh = (b2 & 0xf) + 1;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000406 memGraphics.setColor(fg);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000407 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000408 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000409 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000410 memGraphics.setColor(fg);
411 for (int j = 0; j < nSubrects; j++) {
412 b1 = rfb.is.readUnsignedByte();
413 b2 = rfb.is.readUnsignedByte();
414 sx = tx + (b1 >> 4);
415 sy = ty + (b1 & 0xf);
416 sw = (b2 >> 4) + 1;
417 sh = (b2 & 0xf) + 1;
418 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000419 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000420 }
421
422 }
423 // Finished with a row of tiles, now let's show it.
424 scheduleRepaint(rx, ty, rw, th);
425 }
426 break;
427 }
428
429 case RfbProto.EncodingZlib:
430 {
431 int nBytes = rfb.is.readInt();
432
433 if (zlibBuf == null || zlibBufLen < nBytes) {
434 zlibBufLen = nBytes * 2;
435 zlibBuf = new byte[zlibBufLen];
436 }
437
438 rfb.is.readFully(zlibBuf, 0, nBytes);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000439 zlibInflater.setInput(zlibBuf, 0, nBytes);
440
441 handleZlibRect(rfb.updateRectX, rfb.updateRectY,
442 rfb.updateRectW, rfb.updateRectH);
443
444 break;
445 }
446
447 case RfbProto.EncodingTight:
448 {
449 handleTightRect(rfb.updateRectX, rfb.updateRectY,
450 rfb.updateRectW, rfb.updateRectH);
451
452 break;
453 }
454
455 default:
456 throw new IOException("Unknown RFB rectangle encoding " +
457 rfb.updateRectEncoding);
458 }
459
460 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000461 break;
462
463 case RfbProto.SetColourMapEntries:
464 throw new IOException("Can't handle SetColourMapEntries message");
465
466 case RfbProto.Bell:
467 Toolkit.getDefaultToolkit().beep();
468 break;
469
470 case RfbProto.ServerCutText:
471 String s = rfb.readServerCutText();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000472 break;
473
474 default:
475 throw new IOException("Unknown RFB message type " + msgType);
476 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000477
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000478 player.updatePos();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000479 }
480 }
481
482
483 //
484 // Handle a raw rectangle.
485 //
486
487 void handleRawRect(int x, int y, int w, int h) throws IOException {
488
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000489 byte[] buf = new byte[w * 4];
490 int i, offset;
491 for (int dy = y; dy < y + h; dy++) {
492 rfb.is.readFully(buf);
493 offset = dy * rfb.framebufferWidth + x;
494 for (i = 0; i < w; i++) {
495 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000496 (buf[i * 4 + 2] & 0xFF) << 16 |
497 (buf[i * 4 + 1] & 0xFF) << 8 |
498 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000499 }
500 }
501
502 handleUpdatedPixels(x, y, w, h);
503 scheduleRepaint(x, y, w, h);
504 }
505
506
507 //
508 // Handle a Zlib-encoded rectangle.
509 //
510
511 void handleZlibRect(int x, int y, int w, int h)
512 throws IOException {
513
514 try {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000515 byte[] buf = new byte[w * 4];
516 int i, offset;
517 for (int dy = y; dy < y + h; dy++) {
518 zlibInflater.inflate(buf);
519 offset = dy * rfb.framebufferWidth + x;
520 for (i = 0; i < w; i++) {
521 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000522 (buf[i * 4 + 2] & 0xFF) << 16 |
523 (buf[i * 4 + 1] & 0xFF) << 8 |
524 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000525 }
526 }
527 }
528 catch (DataFormatException dfe) {
529 throw new IOException(dfe.toString());
530 }
531
532 handleUpdatedPixels(x, y, w, h);
533 scheduleRepaint(x, y, w, h);
534 }
535
536
537 //
538 // Handle a tight rectangle.
539 //
540
541 void handleTightRect(int x, int y, int w, int h) throws IOException {
542
543 int comp_ctl = rfb.is.readUnsignedByte();
544
545 // Flush zlib streams if we are told by the server to do so.
546 for (int stream_id = 0; stream_id < 4; stream_id++) {
547 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
548 tightInflaters[stream_id] = null;
549 }
550 comp_ctl >>= 1;
551 }
552
553 // Check correctness of subencoding value.
554 if (comp_ctl > rfb.TightMaxSubencoding) {
555 throw new IOException("Incorrect tight subencoding: " + comp_ctl);
556 }
557
558 // Handle solid-color rectangles.
559 if (comp_ctl == rfb.TightFill) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000560 byte[] buf = new byte[3];
561 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000562 Color bg = new Color(buf[0] & 0xFF, buf[1] & 0xFF, buf[2] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000563 memGraphics.setColor(bg);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000564 memGraphics.fillRect(x, y, w, h);
565 scheduleRepaint(x, y, w, h);
566 return;
567 }
568
569 if (comp_ctl == rfb.TightJpeg) {
570
571 // Read JPEG data.
572 byte[] jpegData = new byte[rfb.readCompactLen()];
573 rfb.is.readFully(jpegData);
574
575 // Create an Image object from the JPEG data.
576 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
577
578 // Remember the rectangle where the image should be drawn.
579 jpegRect = new Rectangle(x, y, w, h);
580
581 // Let the imageUpdate() method do the actual drawing, here just
582 // wait until the image is fully loaded and drawn.
583 synchronized(jpegRect) {
584 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
585 try {
586 // Wait no longer than three seconds.
587 jpegRect.wait(3000);
588 } catch (InterruptedException e) {
589 throw new IOException("Interrupted while decoding JPEG image");
590 }
591 }
592
593 // Done, jpegRect is not needed any more.
594 jpegRect = null;
595 return;
596
597 }
598
599 // Read filter id and parameters.
600 int numColors = 0, rowSize = w;
601 byte[] palette8 = new byte[2];
602 int[] palette24 = new int[256];
603 boolean useGradient = false;
604 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
605 int filter_id = rfb.is.readUnsignedByte();
606 if (filter_id == rfb.TightFilterPalette) {
607 numColors = rfb.is.readUnsignedByte() + 1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000608 byte[] buf = new byte[numColors * 3];
609 rfb.is.readFully(buf);
610 for (int i = 0; i < numColors; i++) {
611 palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
612 (buf[i * 3 + 1] & 0xFF) << 8 |
613 (buf[i * 3 + 2] & 0xFF));
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000614 }
615 if (numColors == 2)
616 rowSize = (w + 7) / 8;
617 } else if (filter_id == rfb.TightFilterGradient) {
618 useGradient = true;
619 } else if (filter_id != rfb.TightFilterCopy) {
620 throw new IOException("Incorrect tight filter id: " + filter_id);
621 }
622 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000623 if (numColors == 0)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000624 rowSize *= 3;
625
626 // Read, optionally uncompress and decode data.
627 int dataSize = h * rowSize;
628 if (dataSize < rfb.TightMinToCompress) {
629 // Data size is small - not compressed with zlib.
630 if (numColors != 0) {
631 // Indexed colors.
632 byte[] indexedData = new byte[dataSize];
633 rfb.is.readFully(indexedData);
634 if (numColors == 2) {
635 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000636 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000637 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000638 // 3..255 colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000639 int i = 0;
640 for (int dy = y; dy < y + h; dy++) {
641 for (int dx = x; dx < x + w; dx++) {
642 pixels24[dy * rfb.framebufferWidth + dx] =
643 palette24[indexedData[i++] & 0xFF];
644 }
645 }
646 }
647 } else if (useGradient) {
648 // "Gradient"-processed data
649 byte[] buf = new byte[w * h * 3];
650 rfb.is.readFully(buf);
651 decodeGradientData(x, y, w, h, buf);
652 } else {
653 // Raw truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000654 byte[] buf = new byte[w * 3];
655 int i, offset;
656 for (int dy = y; dy < y + h; dy++) {
657 rfb.is.readFully(buf);
658 offset = dy * rfb.framebufferWidth + x;
659 for (i = 0; i < w; i++) {
660 pixels24[offset + i] =
661 (buf[i * 3] & 0xFF) << 16 |
662 (buf[i * 3 + 1] & 0xFF) << 8 |
663 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000664 }
665 }
666 }
667 } else {
668 // Data was compressed with zlib.
669 int zlibDataLen = rfb.readCompactLen();
670 byte[] zlibData = new byte[zlibDataLen];
671 rfb.is.readFully(zlibData);
672 int stream_id = comp_ctl & 0x03;
673 if (tightInflaters[stream_id] == null) {
674 tightInflaters[stream_id] = new Inflater();
675 }
676 Inflater myInflater = tightInflaters[stream_id];
677 myInflater.setInput(zlibData);
678 try {
679 if (numColors != 0) {
680 // Indexed colors.
681 byte[] indexedData = new byte[dataSize];
682 myInflater.inflate(indexedData);
683 if (numColors == 2) {
684 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000685 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000686 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000687 // More than two colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000688 int i = 0;
689 for (int dy = y; dy < y + h; dy++) {
690 for (int dx = x; dx < x + w; dx++) {
691 pixels24[dy * rfb.framebufferWidth + dx] =
692 palette24[indexedData[i++] & 0xFF];
693 }
694 }
695 }
696 } else if (useGradient) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000697 // Compressed "Gradient"-filtered data.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000698 byte[] buf = new byte[w * h * 3];
699 myInflater.inflate(buf);
700 decodeGradientData(x, y, w, h, buf);
701 } else {
702 // Compressed truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000703 byte[] buf = new byte[w * 3];
704 int i, offset;
705 for (int dy = y; dy < y + h; dy++) {
706 myInflater.inflate(buf);
707 offset = dy * rfb.framebufferWidth + x;
708 for (i = 0; i < w; i++) {
709 pixels24[offset + i] =
710 (buf[i * 3] & 0xFF) << 16 |
711 (buf[i * 3 + 1] & 0xFF) << 8 |
712 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000713 }
714 }
715 }
716 }
717 catch(DataFormatException dfe) {
718 throw new IOException(dfe.toString());
719 }
720 }
721
722 handleUpdatedPixels(x, y, w, h);
723 scheduleRepaint(x, y, w, h);
724 }
725
726 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000727 // Decode 1bpp-encoded bi-color rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000728 //
729
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000730 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette)
731 throws IOException {
732
733 int dx, dy, n;
734 int i = y * rfb.framebufferWidth + x;
735 int rowBytes = (w + 7) / 8;
736 byte b;
737
738 for (dy = 0; dy < h; dy++) {
739 for (dx = 0; dx < w / 8; dx++) {
740 b = src[dy*rowBytes+dx];
741 for (n = 7; n >= 0; n--)
742 pixels24[i++] = palette[b >> n & 1];
743 }
744 for (n = 7; n >= 8 - w % 8; n--) {
745 pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
746 }
747 i += (rfb.framebufferWidth - w);
748 }
749 }
750
751 //
752 // Decode data processed with the "Gradient" filter.
753 //
754
755 void decodeGradientData (int x, int y, int w, int h, byte[] buf)
756 throws IOException {
757
758 int dx, dy, c;
759 byte[] prevRow = new byte[w * 3];
760 byte[] thisRow = new byte[w * 3];
761 byte[] pix = new byte[3];
762 int[] est = new int[3];
763
764 int offset = y * rfb.framebufferWidth + x;
765
766 for (dy = 0; dy < h; dy++) {
767
768 /* First pixel in a row */
769 for (c = 0; c < 3; c++) {
770 pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
771 thisRow[c] = pix[c];
772 }
773 pixels24[offset++] =
774 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
775
776 /* Remaining pixels of a row */
777 for (dx = 1; dx < w; dx++) {
778 for (c = 0; c < 3; c++) {
779 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
780 (prevRow[(dx-1) * 3 + c] & 0xFF));
781 if (est[c] > 0xFF) {
782 est[c] = 0xFF;
783 } else if (est[c] < 0x00) {
784 est[c] = 0x00;
785 }
786 pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
787 thisRow[dx * 3 + c] = pix[c];
788 }
789 pixels24[offset++] =
790 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
791 }
792
793 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
794 offset += (rfb.framebufferWidth - w);
795 }
796 }
797
798
799 //
800 // Display newly updated area of pixels.
801 //
802
803 void handleUpdatedPixels(int x, int y, int w, int h) {
804
805 // Draw updated pixels of the off-screen image.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000806
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000807 pixelsSource.newPixels(x, y, w, h);
808 memGraphics.setClip(x, y, w, h);
809 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
810 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
811 }
812
813 //
814 // Tell JVM to repaint specified desktop area.
815 //
816
817 void scheduleRepaint(int x, int y, int w, int h) {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000818 if (player.fbsStream.isSeeking()) {
819 // Do nothing, and remember we are seeking.
820 seekMode = true;
821 } else {
822 if (seekMode) {
823 // Full-screen immediate repaint after seeking.
824 repaint();
825 } else {
826 // Usual incremental repaint in playback mode.
827 repaint(player.deferScreenUpdates, x, y, w, h);
828 }
829 seekMode = false;
830 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000831 }
832
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000833}