blob: e0d9994d2e7172f813e485649777131fc775001a [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 Kaplinsky1215b992008-04-18 09:51:44 +000063 //
64 // The constructor.
65 //
66
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000067 VncCanvas(RfbPlayer player) throws IOException {
68 this.player = player;
69 rfb = player.rfb;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000070
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000071 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
72
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000073 updateFramebufferSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000074 }
75
76 //
77 // Callback methods to determine geometry of our Component.
78 //
79
80 public Dimension getPreferredSize() {
81 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
82 }
83
84 public Dimension getMinimumSize() {
85 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
86 }
87
88 public Dimension getMaximumSize() {
89 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
90 }
91
92 //
93 // All painting is performed here.
94 //
95
96 public void update(Graphics g) {
97 paint(g);
98 }
99
100 public void paint(Graphics g) {
101 synchronized(memImage) {
102 g.drawImage(memImage, 0, 0, null);
103 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000104 }
105
106 //
107 // Override the ImageObserver interface method to handle drawing of
108 // JPEG-encoded data.
109 //
110
111 public boolean imageUpdate(Image img, int infoflags,
112 int x, int y, int width, int height) {
113 if ((infoflags & (ALLBITS | ABORT)) == 0) {
114 return true; // We need more image data.
115 } else {
116 // If the whole image is available, draw it now.
117 if ((infoflags & ALLBITS) != 0) {
118 if (jpegRect != null) {
119 synchronized(jpegRect) {
120 memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null);
121 scheduleRepaint(jpegRect.x, jpegRect.y,
122 jpegRect.width, jpegRect.height);
123 jpegRect.notify();
124 }
125 }
126 }
127 return false; // All image data was processed.
128 }
129 }
130
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000131 void updateFramebufferSize() {
132
133 // Useful shortcuts.
134 int fbWidth = rfb.framebufferWidth;
135 int fbHeight = rfb.framebufferHeight;
136
137 // Create new off-screen image either if it does not exist, or if
138 // its geometry should be changed. It's not necessary to replace
139 // existing image if only pixel format should be changed.
140 if (memImage == null) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000141 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000142 memGraphics = memImage.getGraphics();
143 } else if (memImage.getWidth(null) != fbWidth ||
144 memImage.getHeight(null) != fbHeight) {
145 synchronized(memImage) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000146 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000147 memGraphics = memImage.getGraphics();
148 }
149 }
150
151 // Images with raw pixels should be re-allocated on every change
152 // of geometry or pixel format.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000153 pixels24 = new int[fbWidth * fbHeight];
154 pixelsSource =
155 new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000156 pixelsSource.setAnimated(true);
157 rawPixelsImage = createImage(pixelsSource);
158
159 // Update the size of desktop containers.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000160 if (player.inSeparateFrame) {
161 if (player.desktopScrollPane != null)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000162 resizeDesktopFrame();
163 } else {
164 setSize(fbWidth, fbHeight);
165 }
166 }
167
168 void resizeDesktopFrame() {
169 setSize(rfb.framebufferWidth, rfb.framebufferHeight);
170
171 // FIXME: Find a better way to determine correct size of a
172 // ScrollPane. -- const
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000173 Insets insets = player.desktopScrollPane.getInsets();
174 player.desktopScrollPane.setSize(rfb.framebufferWidth +
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000175 2 * Math.min(insets.left, insets.right),
176 rfb.framebufferHeight +
177 2 * Math.min(insets.top, insets.bottom));
178
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000179 player.vncFrame.pack();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000180
181 // Try to limit the frame size to the screen size.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000182 Dimension screenSize = player.vncFrame.getToolkit().getScreenSize();
183 Dimension frameSize = player.vncFrame.getSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000184 Dimension newSize = frameSize;
185 boolean needToResizeFrame = false;
186 if (frameSize.height > screenSize.height) {
187 newSize.height = screenSize.height;
188 needToResizeFrame = true;
189 }
190 if (frameSize.width > screenSize.width) {
191 newSize.width = screenSize.width;
192 needToResizeFrame = true;
193 }
194 if (needToResizeFrame) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000195 player.vncFrame.setSize(newSize);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000196 }
197
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000198 player.desktopScrollPane.doLayout();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000199 }
200
201 //
202 // processNormalProtocol() - executed by the rfbThread to deal with the
203 // RFB socket.
204 //
205
206 public void processNormalProtocol() throws IOException {
207
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000208 zlibInflater = new Inflater();
209 tightInflaters = new Inflater[4];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000210
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000211 player.updatePos();
212
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000213 // Main dispatch loop.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000214
215 while (true) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000216
217 while (player.getMode() != player.MODE_PLAYBACK) {
218 synchronized(this) {
219 try {
220 wait();
221 } catch (InterruptedException e) {
222 }
223 }
224 if (player.getMode() == player.MODE_STOPPED) {
225 throw new EOFException("Playback stopped");
226 }
227 if (player.getMode() == player.MODE_PLAYBACK) {
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000228 player.fbsStream.resumePlayback();
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000229 }
230 }
231
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000232 int msgType = rfb.readServerMessageType();
233
234 switch (msgType) {
235 case RfbProto.FramebufferUpdate:
236 rfb.readFramebufferUpdate();
237
238 for (int i = 0; i < rfb.updateNRects; i++) {
239 rfb.readFramebufferUpdateRectHdr();
240
241 if (rfb.updateRectEncoding == rfb.EncodingLastRect)
242 break;
243
244 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
245 rfb.setFramebufferSize(rfb.updateRectW, rfb.updateRectH);
246 updateFramebufferSize();
247 break;
248 }
249
250 if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
251 rfb.updateRectEncoding == rfb.EncodingRichCursor) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000252 throw new IOException("Sorry, no support for" +
253 " cursor shape updates yet");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000254 }
255
256 switch (rfb.updateRectEncoding) {
257
258 case RfbProto.EncodingRaw:
259 {
260 handleRawRect(rfb.updateRectX, rfb.updateRectY,
261 rfb.updateRectW, rfb.updateRectH);
262 break;
263 }
264
265 case RfbProto.EncodingCopyRect:
266 {
267 rfb.readCopyRect();
268
269 int sx = rfb.copyRectSrcX, sy = rfb.copyRectSrcY;
270 int rx = rfb.updateRectX, ry = rfb.updateRectY;
271 int rw = rfb.updateRectW, rh = rfb.updateRectH;
272
273 memGraphics.copyArea(sx, sy, rw, rh, rx - sx, ry - sy);
274
275 scheduleRepaint(rx, ry, rw, rh);
276 break;
277 }
278
279 case RfbProto.EncodingRRE:
280 {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000281 byte[] buf = new byte[4];
282
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000283 int rx = rfb.updateRectX, ry = rfb.updateRectY;
284 int rw = rfb.updateRectW, rh = rfb.updateRectH;
285 int nSubrects = rfb.is.readInt();
286 int x, y, w, h;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000287
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000288 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000289 Color pixel = new Color(buf[2] & 0xFF,
290 buf[1] & 0xFF,
291 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000292 memGraphics.setColor(pixel);
293 memGraphics.fillRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000294
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000295 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000296 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000297 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000298 x = rx + rfb.is.readUnsignedShort();
299 y = ry + rfb.is.readUnsignedShort();
300 w = rfb.is.readUnsignedShort();
301 h = rfb.is.readUnsignedShort();
302
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000303 memGraphics.setColor(pixel);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000304 memGraphics.fillRect(x, y, w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000305 }
306
307 scheduleRepaint(rx, ry, rw, rh);
308 break;
309 }
310
311 case RfbProto.EncodingCoRRE:
312 {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000313 byte[] buf = new byte[4];
314
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000315 int rx = rfb.updateRectX, ry = rfb.updateRectY;
316 int rw = rfb.updateRectW, rh = rfb.updateRectH;
317 int nSubrects = rfb.is.readInt();
318 int x, y, w, h;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000319
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000320 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000321 Color pixel = new Color(buf[2] & 0xFF,
322 buf[1] & 0xFF,
323 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000324 memGraphics.setColor(pixel);
325 memGraphics.fillRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000326
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000327 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky2258f122002-05-20 13:10:44 +0000328 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000329 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000330 x = rx + rfb.is.readUnsignedByte();
331 y = ry + rfb.is.readUnsignedByte();
332 w = rfb.is.readUnsignedByte();
333 h = rfb.is.readUnsignedByte();
334
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000335 memGraphics.setColor(pixel);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000336 memGraphics.fillRect(x, y, w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000337 }
338
339 scheduleRepaint(rx, ry, rw, rh);
340 break;
341 }
342
343 case RfbProto.EncodingHextile:
344 {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000345 byte[] buf = new byte[256 * 4];
346
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000347 int rx = rfb.updateRectX, ry = rfb.updateRectY;
348 int rw = rfb.updateRectW, rh = rfb.updateRectH;
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000349 Color bg = new Color(0, 0, 0), fg = new Color(0, 0, 0);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000350
351 for (int ty = ry; ty < ry + rh; ty += 16) {
352
353 int th = 16;
354 if (ry + rh - ty < 16)
355 th = ry + rh - ty;
356
357 for (int tx = rx; tx < rx + rw; tx += 16) {
358
359 int tw = 16;
360 if (rx + rw - tx < 16)
361 tw = rx + rw - tx;
362
363 int subencoding = rfb.is.readUnsignedByte();
364
365 // Is it a raw-encoded sub-rectangle?
366 if ((subencoding & rfb.HextileRaw) != 0) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000367 int count, offset;
368 for (int j = ty; j < ty + th; j++) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000369 rfb.is.readFully(buf, 0, tw * 4);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000370 offset = j * rfb.framebufferWidth + tx;
371 for (count = 0; count < tw; count++) {
372 pixels24[offset + count] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000373 (buf[count * 4 + 2] & 0xFF) << 16 |
374 (buf[count * 4 + 1] & 0xFF) << 8 |
375 (buf[count * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000376 }
377 }
378 handleUpdatedPixels(tx, ty, tw, th);
379 continue;
380 }
381
382 // Read and draw the background if specified.
383 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000384 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000385 bg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000386 }
387 memGraphics.setColor(bg);
388 memGraphics.fillRect(tx, ty, tw, th);
389
390 // Read the foreground color if specified.
391 if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000392 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000393 fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000394 }
395
396 // Done with this tile if there is no sub-rectangles.
397 if ((subencoding & rfb.HextileAnySubrects) == 0)
398 continue;
399
400 int nSubrects = rfb.is.readUnsignedByte();
401
402 int b1, b2, sx, sy, sw, sh;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000403 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
404 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000405 rfb.is.readFully(buf, 0, 4);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000406 fg = new Color(buf[2] & 0xFF,
407 buf[1] & 0xFF,
408 buf[0] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000409 b1 = rfb.is.readUnsignedByte();
410 b2 = rfb.is.readUnsignedByte();
411 sx = tx + (b1 >> 4);
412 sy = ty + (b1 & 0xf);
413 sw = (b2 >> 4) + 1;
414 sh = (b2 & 0xf) + 1;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000415 memGraphics.setColor(fg);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000416 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000417 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000418 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000419 memGraphics.setColor(fg);
420 for (int j = 0; j < nSubrects; j++) {
421 b1 = rfb.is.readUnsignedByte();
422 b2 = rfb.is.readUnsignedByte();
423 sx = tx + (b1 >> 4);
424 sy = ty + (b1 & 0xf);
425 sw = (b2 >> 4) + 1;
426 sh = (b2 & 0xf) + 1;
427 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000428 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000429 }
430
431 }
432 // Finished with a row of tiles, now let's show it.
433 scheduleRepaint(rx, ty, rw, th);
434 }
435 break;
436 }
437
438 case RfbProto.EncodingZlib:
439 {
440 int nBytes = rfb.is.readInt();
441
442 if (zlibBuf == null || zlibBufLen < nBytes) {
443 zlibBufLen = nBytes * 2;
444 zlibBuf = new byte[zlibBufLen];
445 }
446
447 rfb.is.readFully(zlibBuf, 0, nBytes);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000448 zlibInflater.setInput(zlibBuf, 0, nBytes);
449
450 handleZlibRect(rfb.updateRectX, rfb.updateRectY,
451 rfb.updateRectW, rfb.updateRectH);
452
453 break;
454 }
455
456 case RfbProto.EncodingTight:
457 {
458 handleTightRect(rfb.updateRectX, rfb.updateRectY,
459 rfb.updateRectW, rfb.updateRectH);
460
461 break;
462 }
463
464 default:
465 throw new IOException("Unknown RFB rectangle encoding " +
466 rfb.updateRectEncoding);
467 }
468
469 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000470 break;
471
472 case RfbProto.SetColourMapEntries:
473 throw new IOException("Can't handle SetColourMapEntries message");
474
475 case RfbProto.Bell:
476 Toolkit.getDefaultToolkit().beep();
477 break;
478
479 case RfbProto.ServerCutText:
480 String s = rfb.readServerCutText();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000481 break;
482
483 default:
484 throw new IOException("Unknown RFB message type " + msgType);
485 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000486
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000487 player.updatePos();
488
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000489 if (player.getMode() == player.MODE_STOPPED) {
490 throw new EOFException("Playback stopped");
491 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000492 }
493 }
494
495
496 //
497 // Handle a raw rectangle.
498 //
499
500 void handleRawRect(int x, int y, int w, int h) throws IOException {
501
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000502 byte[] buf = new byte[w * 4];
503 int i, offset;
504 for (int dy = y; dy < y + h; dy++) {
505 rfb.is.readFully(buf);
506 offset = dy * rfb.framebufferWidth + x;
507 for (i = 0; i < w; i++) {
508 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000509 (buf[i * 4 + 2] & 0xFF) << 16 |
510 (buf[i * 4 + 1] & 0xFF) << 8 |
511 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000512 }
513 }
514
515 handleUpdatedPixels(x, y, w, h);
516 scheduleRepaint(x, y, w, h);
517 }
518
519
520 //
521 // Handle a Zlib-encoded rectangle.
522 //
523
524 void handleZlibRect(int x, int y, int w, int h)
525 throws IOException {
526
527 try {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000528 byte[] buf = new byte[w * 4];
529 int i, offset;
530 for (int dy = y; dy < y + h; dy++) {
531 zlibInflater.inflate(buf);
532 offset = dy * rfb.framebufferWidth + x;
533 for (i = 0; i < w; i++) {
534 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000535 (buf[i * 4 + 2] & 0xFF) << 16 |
536 (buf[i * 4 + 1] & 0xFF) << 8 |
537 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000538 }
539 }
540 }
541 catch (DataFormatException dfe) {
542 throw new IOException(dfe.toString());
543 }
544
545 handleUpdatedPixels(x, y, w, h);
546 scheduleRepaint(x, y, w, h);
547 }
548
549
550 //
551 // Handle a tight rectangle.
552 //
553
554 void handleTightRect(int x, int y, int w, int h) throws IOException {
555
556 int comp_ctl = rfb.is.readUnsignedByte();
557
558 // Flush zlib streams if we are told by the server to do so.
559 for (int stream_id = 0; stream_id < 4; stream_id++) {
560 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
561 tightInflaters[stream_id] = null;
562 }
563 comp_ctl >>= 1;
564 }
565
566 // Check correctness of subencoding value.
567 if (comp_ctl > rfb.TightMaxSubencoding) {
568 throw new IOException("Incorrect tight subencoding: " + comp_ctl);
569 }
570
571 // Handle solid-color rectangles.
572 if (comp_ctl == rfb.TightFill) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000573 byte[] buf = new byte[3];
574 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000575 Color bg = new Color(buf[0] & 0xFF, buf[1] & 0xFF, buf[2] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000576 memGraphics.setColor(bg);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000577 memGraphics.fillRect(x, y, w, h);
578 scheduleRepaint(x, y, w, h);
579 return;
580 }
581
582 if (comp_ctl == rfb.TightJpeg) {
583
584 // Read JPEG data.
585 byte[] jpegData = new byte[rfb.readCompactLen()];
586 rfb.is.readFully(jpegData);
587
588 // Create an Image object from the JPEG data.
589 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
590
591 // Remember the rectangle where the image should be drawn.
592 jpegRect = new Rectangle(x, y, w, h);
593
594 // Let the imageUpdate() method do the actual drawing, here just
595 // wait until the image is fully loaded and drawn.
596 synchronized(jpegRect) {
597 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
598 try {
599 // Wait no longer than three seconds.
600 jpegRect.wait(3000);
601 } catch (InterruptedException e) {
602 throw new IOException("Interrupted while decoding JPEG image");
603 }
604 }
605
606 // Done, jpegRect is not needed any more.
607 jpegRect = null;
608 return;
609
610 }
611
612 // Read filter id and parameters.
613 int numColors = 0, rowSize = w;
614 byte[] palette8 = new byte[2];
615 int[] palette24 = new int[256];
616 boolean useGradient = false;
617 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
618 int filter_id = rfb.is.readUnsignedByte();
619 if (filter_id == rfb.TightFilterPalette) {
620 numColors = rfb.is.readUnsignedByte() + 1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000621 byte[] buf = new byte[numColors * 3];
622 rfb.is.readFully(buf);
623 for (int i = 0; i < numColors; i++) {
624 palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
625 (buf[i * 3 + 1] & 0xFF) << 8 |
626 (buf[i * 3 + 2] & 0xFF));
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000627 }
628 if (numColors == 2)
629 rowSize = (w + 7) / 8;
630 } else if (filter_id == rfb.TightFilterGradient) {
631 useGradient = true;
632 } else if (filter_id != rfb.TightFilterCopy) {
633 throw new IOException("Incorrect tight filter id: " + filter_id);
634 }
635 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000636 if (numColors == 0)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000637 rowSize *= 3;
638
639 // Read, optionally uncompress and decode data.
640 int dataSize = h * rowSize;
641 if (dataSize < rfb.TightMinToCompress) {
642 // Data size is small - not compressed with zlib.
643 if (numColors != 0) {
644 // Indexed colors.
645 byte[] indexedData = new byte[dataSize];
646 rfb.is.readFully(indexedData);
647 if (numColors == 2) {
648 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000649 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000650 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000651 // 3..255 colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000652 int i = 0;
653 for (int dy = y; dy < y + h; dy++) {
654 for (int dx = x; dx < x + w; dx++) {
655 pixels24[dy * rfb.framebufferWidth + dx] =
656 palette24[indexedData[i++] & 0xFF];
657 }
658 }
659 }
660 } else if (useGradient) {
661 // "Gradient"-processed data
662 byte[] buf = new byte[w * h * 3];
663 rfb.is.readFully(buf);
664 decodeGradientData(x, y, w, h, buf);
665 } else {
666 // Raw truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000667 byte[] buf = new byte[w * 3];
668 int i, offset;
669 for (int dy = y; dy < y + h; dy++) {
670 rfb.is.readFully(buf);
671 offset = dy * rfb.framebufferWidth + x;
672 for (i = 0; i < w; i++) {
673 pixels24[offset + i] =
674 (buf[i * 3] & 0xFF) << 16 |
675 (buf[i * 3 + 1] & 0xFF) << 8 |
676 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000677 }
678 }
679 }
680 } else {
681 // Data was compressed with zlib.
682 int zlibDataLen = rfb.readCompactLen();
683 byte[] zlibData = new byte[zlibDataLen];
684 rfb.is.readFully(zlibData);
685 int stream_id = comp_ctl & 0x03;
686 if (tightInflaters[stream_id] == null) {
687 tightInflaters[stream_id] = new Inflater();
688 }
689 Inflater myInflater = tightInflaters[stream_id];
690 myInflater.setInput(zlibData);
691 try {
692 if (numColors != 0) {
693 // Indexed colors.
694 byte[] indexedData = new byte[dataSize];
695 myInflater.inflate(indexedData);
696 if (numColors == 2) {
697 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000698 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000699 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000700 // More than two colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000701 int i = 0;
702 for (int dy = y; dy < y + h; dy++) {
703 for (int dx = x; dx < x + w; dx++) {
704 pixels24[dy * rfb.framebufferWidth + dx] =
705 palette24[indexedData[i++] & 0xFF];
706 }
707 }
708 }
709 } else if (useGradient) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000710 // Compressed "Gradient"-filtered data.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000711 byte[] buf = new byte[w * h * 3];
712 myInflater.inflate(buf);
713 decodeGradientData(x, y, w, h, buf);
714 } else {
715 // Compressed truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000716 byte[] buf = new byte[w * 3];
717 int i, offset;
718 for (int dy = y; dy < y + h; dy++) {
719 myInflater.inflate(buf);
720 offset = dy * rfb.framebufferWidth + x;
721 for (i = 0; i < w; i++) {
722 pixels24[offset + i] =
723 (buf[i * 3] & 0xFF) << 16 |
724 (buf[i * 3 + 1] & 0xFF) << 8 |
725 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000726 }
727 }
728 }
729 }
730 catch(DataFormatException dfe) {
731 throw new IOException(dfe.toString());
732 }
733 }
734
735 handleUpdatedPixels(x, y, w, h);
736 scheduleRepaint(x, y, w, h);
737 }
738
739 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000740 // Decode 1bpp-encoded bi-color rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000741 //
742
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000743 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette)
744 throws IOException {
745
746 int dx, dy, n;
747 int i = y * rfb.framebufferWidth + x;
748 int rowBytes = (w + 7) / 8;
749 byte b;
750
751 for (dy = 0; dy < h; dy++) {
752 for (dx = 0; dx < w / 8; dx++) {
753 b = src[dy*rowBytes+dx];
754 for (n = 7; n >= 0; n--)
755 pixels24[i++] = palette[b >> n & 1];
756 }
757 for (n = 7; n >= 8 - w % 8; n--) {
758 pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
759 }
760 i += (rfb.framebufferWidth - w);
761 }
762 }
763
764 //
765 // Decode data processed with the "Gradient" filter.
766 //
767
768 void decodeGradientData (int x, int y, int w, int h, byte[] buf)
769 throws IOException {
770
771 int dx, dy, c;
772 byte[] prevRow = new byte[w * 3];
773 byte[] thisRow = new byte[w * 3];
774 byte[] pix = new byte[3];
775 int[] est = new int[3];
776
777 int offset = y * rfb.framebufferWidth + x;
778
779 for (dy = 0; dy < h; dy++) {
780
781 /* First pixel in a row */
782 for (c = 0; c < 3; c++) {
783 pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
784 thisRow[c] = pix[c];
785 }
786 pixels24[offset++] =
787 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
788
789 /* Remaining pixels of a row */
790 for (dx = 1; dx < w; dx++) {
791 for (c = 0; c < 3; c++) {
792 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
793 (prevRow[(dx-1) * 3 + c] & 0xFF));
794 if (est[c] > 0xFF) {
795 est[c] = 0xFF;
796 } else if (est[c] < 0x00) {
797 est[c] = 0x00;
798 }
799 pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
800 thisRow[dx * 3 + c] = pix[c];
801 }
802 pixels24[offset++] =
803 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
804 }
805
806 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
807 offset += (rfb.framebufferWidth - w);
808 }
809 }
810
811
812 //
813 // Display newly updated area of pixels.
814 //
815
816 void handleUpdatedPixels(int x, int y, int w, int h) {
817
818 // Draw updated pixels of the off-screen image.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000819
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000820 pixelsSource.newPixels(x, y, w, h);
821 memGraphics.setClip(x, y, w, h);
822 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
823 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
824 }
825
826 //
827 // Tell JVM to repaint specified desktop area.
828 //
829
830 void scheduleRepaint(int x, int y, int w, int h) {
Constantin Kaplinsky30f786a2002-05-29 10:59:52 +0000831 // Request repaint if not in the seeking mode.
832 if (!player.fbsStream.isSeeking())
833 repaint(player.deferScreenUpdates, x, y, w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000834 }
835
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000836}