@novnc/novnc 1.2.0 → 1.3.0-g05baf14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE.txt +0 -6
  2. package/README.md +16 -6
  3. package/core/decoders/copyrect.js +5 -0
  4. package/core/decoders/hextile.js +57 -3
  5. package/core/decoders/jpeg.js +141 -0
  6. package/core/decoders/raw.js +12 -2
  7. package/core/decoders/tight.js +24 -8
  8. package/core/decoders/zrle.js +185 -0
  9. package/core/display.js +21 -151
  10. package/core/encodings.js +4 -0
  11. package/core/input/domkeytable.js +25 -21
  12. package/core/input/keyboard.js +22 -127
  13. package/core/input/util.js +18 -35
  14. package/core/input/vkeys.js +0 -1
  15. package/core/input/xtscancodes.js +5 -3
  16. package/core/ra2.js +567 -0
  17. package/core/rfb.js +487 -171
  18. package/core/util/browser.js +0 -17
  19. package/core/util/cursor.js +5 -11
  20. package/core/util/events.js +0 -4
  21. package/core/util/md5.js +79 -0
  22. package/core/websock.js +76 -17
  23. package/docs/API.md +107 -6
  24. package/docs/LIBRARY.md +3 -7
  25. package/lib/base64.js +24 -38
  26. package/lib/decoders/copyrect.js +10 -12
  27. package/lib/decoders/hextile.js +71 -45
  28. package/lib/decoders/jpeg.js +149 -0
  29. package/lib/decoders/raw.js +18 -22
  30. package/lib/decoders/rre.js +7 -18
  31. package/lib/decoders/tight.js +46 -94
  32. package/lib/decoders/tightpng.js +14 -34
  33. package/lib/decoders/zrle.js +188 -0
  34. package/lib/deflator.js +13 -27
  35. package/lib/des.js +26 -39
  36. package/lib/display.js +103 -316
  37. package/lib/encodings.js +7 -8
  38. package/lib/inflator.js +10 -23
  39. package/lib/input/domkeytable.js +240 -208
  40. package/lib/input/fixedkeys.js +10 -5
  41. package/lib/input/gesturehandler.js +88 -155
  42. package/lib/input/keyboard.js +90 -239
  43. package/lib/input/keysym.js +16 -272
  44. package/lib/input/keysymdef.js +7 -9
  45. package/lib/input/util.js +69 -156
  46. package/lib/input/vkeys.js +2 -7
  47. package/lib/input/xtscancodes.js +10 -171
  48. package/lib/ra2.js +1035 -0
  49. package/lib/rfb.js +950 -1150
  50. package/lib/util/browser.js +25 -52
  51. package/lib/util/cursor.js +33 -82
  52. package/lib/util/element.js +3 -5
  53. package/lib/util/events.js +26 -35
  54. package/lib/util/eventtarget.js +8 -17
  55. package/lib/util/int.js +2 -3
  56. package/lib/util/logging.js +3 -21
  57. package/lib/util/md5.js +83 -0
  58. package/lib/util/strings.js +3 -5
  59. package/lib/vendor/pako/lib/utils/common.js +10 -19
  60. package/lib/vendor/pako/lib/zlib/adler32.js +4 -8
  61. package/lib/vendor/pako/lib/zlib/constants.js +4 -7
  62. package/lib/vendor/pako/lib/zlib/crc32.js +6 -13
  63. package/lib/vendor/pako/lib/zlib/deflate.js +304 -708
  64. package/lib/vendor/pako/lib/zlib/gzheader.js +2 -14
  65. package/lib/vendor/pako/lib/zlib/inffast.js +61 -177
  66. package/lib/vendor/pako/lib/zlib/inflate.js +421 -909
  67. package/lib/vendor/pako/lib/zlib/inftrees.js +66 -172
  68. package/lib/vendor/pako/lib/zlib/messages.js +3 -13
  69. package/lib/vendor/pako/lib/zlib/trees.js +250 -592
  70. package/lib/vendor/pako/lib/zlib/zstream.js +3 -19
  71. package/lib/websock.js +122 -112
  72. package/package.json +32 -40
  73. package/core/util/polyfill.js +0 -61
  74. package/lib/util/polyfill.js +0 -72
  75. package/lib/vendor/promise.js +0 -255
package/LICENSE.txt CHANGED
@@ -42,12 +42,6 @@ licenses (all MPL 2.0 compatible):
42
42
 
43
43
  vendor/pako/ : MIT
44
44
 
45
- vendor/browser-es-module-loader/src/ : MIT
46
-
47
- vendor/browser-es-module-loader/dist/ : Various BSD style licenses
48
-
49
- vendor/promise.js : MIT
50
-
51
45
  Any other files not mentioned above are typically marked with
52
46
  a copyright/license header at the top of the file. The default noVNC
53
47
  license is MPL-2.0.
package/README.md CHANGED
@@ -91,7 +91,7 @@ noVNC uses many modern web technologies so a formal requirement list is
91
91
  not available. However these are the minimum versions we are currently
92
92
  aware of:
93
93
 
94
- * Chrome 49, Firefox 44, Safari 11, Opera 36, IE 11, Edge 12
94
+ * Chrome 64, Firefox 79, Safari 13.4, Opera 51, Edge 79
95
95
 
96
96
 
97
97
  ### Server Requirements
@@ -108,13 +108,18 @@ proxy.
108
108
 
109
109
  ### Quick Start
110
110
 
111
- * Use the launch script to automatically download and start websockify, which
111
+ * Use the `novnc_proxy` script to automatically download and start websockify, which
112
112
  includes a mini-webserver and the WebSockets proxy. The `--vnc` option is
113
113
  used to specify the location of a running VNC server:
114
114
 
115
- `./utils/launch.sh --vnc localhost:5901`
115
+ `./utils/novnc_proxy --vnc localhost:5901`
116
+
117
+ * If you don't need to expose the web server to public internet, you can
118
+ bind to localhost:
119
+
120
+ `./utils/novnc_proxy --vnc localhost:5901 --listen localhost:6081`
116
121
 
117
- * Point your browser to the cut-and-paste URL that is output by the launch
122
+ * Point your browser to the cut-and-paste URL that is output by the `novnc_proxy`
118
123
  script. Hit the Connect button, enter a password if the VNC server has one
119
124
  configured, and enjoy!
120
125
 
@@ -123,13 +128,17 @@ Running the command below will install the latest release of noVNC from Snap:
123
128
 
124
129
  `sudo snap install novnc`
125
130
 
126
- #### Running noVNC
131
+ #### Running noVNC from Snap Directly
127
132
 
128
133
  You can run the Snap-package installed novnc directly with, for example:
129
134
 
130
135
  `novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH`
131
136
 
132
- #### Running as a Service (Daemon)
137
+ If you want to use certificate files, due to standard Snap confinement restrictions you need to have them in the /home/\<user\>/snap/novnc/current/ directory. If your username is jsmith an example command would be:
138
+
139
+ `novnc --listen 8443 --cert ~jsmith/snap/novnc/current/self.crt --key ~jsmith/snap/novnc/current/self.key --vnc ubuntu.example.com:5901`
140
+
141
+ #### Running noVNC from Snap as a Service (Daemon)
133
142
  The Snap package also has the capability to run a 'novnc' service which can be
134
143
  configured to listen on multiple ports connecting to multiple VNC servers
135
144
  (effectively a service runing multiple instances of novnc).
@@ -203,6 +212,7 @@ that list and you think you should be, feel free to send a PR to fix that.
203
212
  * UI and Icons : Pierre Ossman, Chris Gordon
204
213
  * Original Logo : Michael Sersen
205
214
  * tight encoding : Michael Tinglof (Mercuri.ca)
215
+ * RealVNC RSA AES authentication : USTC Vlab Team
206
216
 
207
217
  * Included libraries:
208
218
  * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
@@ -15,6 +15,11 @@ export default class CopyRectDecoder {
15
15
 
16
16
  let deltaX = sock.rQshift16();
17
17
  let deltaY = sock.rQshift16();
18
+
19
+ if ((width === 0) || (height === 0)) {
20
+ return true;
21
+ }
22
+
18
23
  display.copyImage(deltaX, deltaY, x, y, width, height);
19
24
 
20
25
  return true;
@@ -13,6 +13,7 @@ export default class HextileDecoder {
13
13
  constructor() {
14
14
  this._tiles = 0;
15
15
  this._lastsubencoding = 0;
16
+ this._tileBuffer = new Uint8Array(16 * 16 * 4);
16
17
  }
17
18
 
18
19
  decodeRect(x, y, width, height, sock, display, depth) {
@@ -87,6 +88,11 @@ export default class HextileDecoder {
87
88
  display.fillRect(tx, ty, tw, th, this._background);
88
89
  }
89
90
  } else if (subencoding & 0x01) { // Raw
91
+ let pixels = tw * th;
92
+ // Max sure the image is fully opaque
93
+ for (let i = 0;i < pixels;i++) {
94
+ rQ[rQi + i * 4 + 3] = 255;
95
+ }
90
96
  display.blitImage(tx, ty, tw, th, rQ, rQi);
91
97
  rQi += bytes - 1;
92
98
  } else {
@@ -99,7 +105,7 @@ export default class HextileDecoder {
99
105
  rQi += 4;
100
106
  }
101
107
 
102
- display.startTile(tx, ty, tw, th, this._background);
108
+ this._startTile(tx, ty, tw, th, this._background);
103
109
  if (subencoding & 0x08) { // AnySubrects
104
110
  let subrects = rQ[rQi];
105
111
  rQi++;
@@ -122,10 +128,10 @@ export default class HextileDecoder {
122
128
  const sw = (wh >> 4) + 1;
123
129
  const sh = (wh & 0x0f) + 1;
124
130
 
125
- display.subTile(sx, sy, sw, sh, color);
131
+ this._subTile(sx, sy, sw, sh, color);
126
132
  }
127
133
  }
128
- display.finishTile();
134
+ this._finishTile(display);
129
135
  }
130
136
  sock.rQi = rQi;
131
137
  this._lastsubencoding = subencoding;
@@ -134,4 +140,52 @@ export default class HextileDecoder {
134
140
 
135
141
  return true;
136
142
  }
143
+
144
+ // start updating a tile
145
+ _startTile(x, y, width, height, color) {
146
+ this._tileX = x;
147
+ this._tileY = y;
148
+ this._tileW = width;
149
+ this._tileH = height;
150
+
151
+ const red = color[0];
152
+ const green = color[1];
153
+ const blue = color[2];
154
+
155
+ const data = this._tileBuffer;
156
+ for (let i = 0; i < width * height * 4; i += 4) {
157
+ data[i] = red;
158
+ data[i + 1] = green;
159
+ data[i + 2] = blue;
160
+ data[i + 3] = 255;
161
+ }
162
+ }
163
+
164
+ // update sub-rectangle of the current tile
165
+ _subTile(x, y, w, h, color) {
166
+ const red = color[0];
167
+ const green = color[1];
168
+ const blue = color[2];
169
+ const xend = x + w;
170
+ const yend = y + h;
171
+
172
+ const data = this._tileBuffer;
173
+ const width = this._tileW;
174
+ for (let j = y; j < yend; j++) {
175
+ for (let i = x; i < xend; i++) {
176
+ const p = (i + (j * width)) * 4;
177
+ data[p] = red;
178
+ data[p + 1] = green;
179
+ data[p + 2] = blue;
180
+ data[p + 3] = 255;
181
+ }
182
+ }
183
+ }
184
+
185
+ // draw the current tile to the screen
186
+ _finishTile(display) {
187
+ display.blitImage(this._tileX, this._tileY,
188
+ this._tileW, this._tileH,
189
+ this._tileBuffer, 0);
190
+ }
137
191
  }
@@ -0,0 +1,141 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ export default class JPEGDecoder {
11
+ constructor() {
12
+ // RealVNC will reuse the quantization tables
13
+ // and Huffman tables, so we need to cache them.
14
+ this._quantTables = [];
15
+ this._huffmanTables = [];
16
+ this._cachedQuantTables = [];
17
+ this._cachedHuffmanTables = [];
18
+
19
+ this._jpegLength = 0;
20
+ this._segments = [];
21
+ }
22
+
23
+ decodeRect(x, y, width, height, sock, display, depth) {
24
+ // A rect of JPEG encodings is simply a JPEG file
25
+ if (!this._parseJPEG(sock.rQslice(0))) {
26
+ return false;
27
+ }
28
+ const data = sock.rQshiftBytes(this._jpegLength);
29
+ if (this._quantTables.length != 0 && this._huffmanTables.length != 0) {
30
+ // If there are quantization tables and Huffman tables in the JPEG
31
+ // image, we can directly render it.
32
+ display.imageRect(x, y, width, height, "image/jpeg", data);
33
+ return true;
34
+ } else {
35
+ // Otherwise we need to insert cached tables.
36
+ const sofIndex = this._segments.findIndex(
37
+ x => x[1] == 0xC0 || x[1] == 0xC2
38
+ );
39
+ if (sofIndex == -1) {
40
+ throw new Error("Illegal JPEG image without SOF");
41
+ }
42
+ let segments = this._segments.slice(0, sofIndex);
43
+ segments = segments.concat(this._quantTables.length ?
44
+ this._quantTables :
45
+ this._cachedQuantTables);
46
+ segments.push(this._segments[sofIndex]);
47
+ segments = segments.concat(this._huffmanTables.length ?
48
+ this._huffmanTables :
49
+ this._cachedHuffmanTables,
50
+ this._segments.slice(sofIndex + 1));
51
+ let length = 0;
52
+ for (let i = 0; i < segments.length; i++) {
53
+ length += segments[i].length;
54
+ }
55
+ const data = new Uint8Array(length);
56
+ length = 0;
57
+ for (let i = 0; i < segments.length; i++) {
58
+ data.set(segments[i], length);
59
+ length += segments[i].length;
60
+ }
61
+ display.imageRect(x, y, width, height, "image/jpeg", data);
62
+ return true;
63
+ }
64
+ }
65
+
66
+ _parseJPEG(buffer) {
67
+ if (this._quantTables.length != 0) {
68
+ this._cachedQuantTables = this._quantTables;
69
+ }
70
+ if (this._huffmanTables.length != 0) {
71
+ this._cachedHuffmanTables = this._huffmanTables;
72
+ }
73
+ this._quantTables = [];
74
+ this._huffmanTables = [];
75
+ this._segments = [];
76
+ let i = 0;
77
+ let bufferLength = buffer.length;
78
+ while (true) {
79
+ let j = i;
80
+ if (j + 2 > bufferLength) {
81
+ return false;
82
+ }
83
+ if (buffer[j] != 0xFF) {
84
+ throw new Error("Illegal JPEG marker received (byte: " +
85
+ buffer[j] + ")");
86
+ }
87
+ const type = buffer[j+1];
88
+ j += 2;
89
+ if (type == 0xD9) {
90
+ this._jpegLength = j;
91
+ this._segments.push(buffer.slice(i, j));
92
+ return true;
93
+ } else if (type == 0xDA) {
94
+ // start of scan
95
+ let hasFoundEndOfScan = false;
96
+ for (let k = j + 3; k + 1 < bufferLength; k++) {
97
+ if (buffer[k] == 0xFF && buffer[k+1] != 0x00 &&
98
+ !(buffer[k+1] >= 0xD0 && buffer[k+1] <= 0xD7)) {
99
+ j = k;
100
+ hasFoundEndOfScan = true;
101
+ break;
102
+ }
103
+ }
104
+ if (!hasFoundEndOfScan) {
105
+ return false;
106
+ }
107
+ this._segments.push(buffer.slice(i, j));
108
+ i = j;
109
+ continue;
110
+ } else if (type >= 0xD0 && type < 0xD9 || type == 0x01) {
111
+ // No length after marker
112
+ this._segments.push(buffer.slice(i, j));
113
+ i = j;
114
+ continue;
115
+ }
116
+ if (j + 2 > bufferLength) {
117
+ return false;
118
+ }
119
+ const length = (buffer[j] << 8) + buffer[j+1] - 2;
120
+ if (length < 0) {
121
+ throw new Error("Illegal JPEG length received (length: " +
122
+ length + ")");
123
+ }
124
+ j += 2;
125
+ if (j + length > bufferLength) {
126
+ return false;
127
+ }
128
+ j += length;
129
+ const segment = buffer.slice(i, j);
130
+ if (type == 0xC4) {
131
+ // Huffman tables
132
+ this._huffmanTables.push(segment);
133
+ } else if (type == 0xDB) {
134
+ // Quantization tables
135
+ this._quantTables.push(segment);
136
+ }
137
+ this._segments.push(segment);
138
+ i = j;
139
+ }
140
+ }
141
+ }
@@ -13,6 +13,10 @@ export default class RawDecoder {
13
13
  }
14
14
 
15
15
  decodeRect(x, y, width, height, sock, display, depth) {
16
+ if ((width === 0) || (height === 0)) {
17
+ return true;
18
+ }
19
+
16
20
  if (this._lines === 0) {
17
21
  this._lines = height;
18
22
  }
@@ -27,23 +31,29 @@ export default class RawDecoder {
27
31
  const curY = y + (height - this._lines);
28
32
  const currHeight = Math.min(this._lines,
29
33
  Math.floor(sock.rQlen / bytesPerLine));
34
+ const pixels = width * currHeight;
35
+
30
36
  let data = sock.rQ;
31
37
  let index = sock.rQi;
32
38
 
33
39
  // Convert data if needed
34
40
  if (depth == 8) {
35
- const pixels = width * currHeight;
36
41
  const newdata = new Uint8Array(pixels * 4);
37
42
  for (let i = 0; i < pixels; i++) {
38
43
  newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
39
44
  newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
40
45
  newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
41
- newdata[i * 4 + 4] = 0;
46
+ newdata[i * 4 + 3] = 255;
42
47
  }
43
48
  data = newdata;
44
49
  index = 0;
45
50
  }
46
51
 
52
+ // Max sure the image is fully opaque
53
+ for (let i = 0; i < pixels; i++) {
54
+ data[index + i * 4 + 3] = 255;
55
+ }
56
+
47
57
  display.blitImage(x, curY, width, currHeight, data, index);
48
58
  sock.rQskipBytes(currHeight * bytesPerLine);
49
59
  this._lines -= currHeight;
@@ -56,7 +56,7 @@ export default class TightDecoder {
56
56
  } else if (this._ctl === 0x0A) {
57
57
  ret = this._pngRect(x, y, width, height,
58
58
  sock, display, depth);
59
- } else if ((this._ctl & 0x80) == 0) {
59
+ } else if ((this._ctl & 0x08) == 0) {
60
60
  ret = this._basicRect(this._ctl, x, y, width, height,
61
61
  sock, display, depth);
62
62
  } else {
@@ -80,7 +80,7 @@ export default class TightDecoder {
80
80
  const rQ = sock.rQ;
81
81
 
82
82
  display.fillRect(x, y, width, height,
83
- [rQ[rQi + 2], rQ[rQi + 1], rQ[rQi]], false);
83
+ [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);
84
84
  sock.rQskipBytes(3);
85
85
 
86
86
  return true;
@@ -148,6 +148,10 @@ export default class TightDecoder {
148
148
  const uncompressedSize = width * height * 3;
149
149
  let data;
150
150
 
151
+ if (uncompressedSize === 0) {
152
+ return true;
153
+ }
154
+
151
155
  if (uncompressedSize < 12) {
152
156
  if (sock.rQwait("TIGHT", uncompressedSize)) {
153
157
  return false;
@@ -165,7 +169,15 @@ export default class TightDecoder {
165
169
  this._zlibs[streamId].setInput(null);
166
170
  }
167
171
 
168
- display.blitRgbImage(x, y, width, height, data, 0, false);
172
+ let rgbx = new Uint8Array(width * height * 4);
173
+ for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
174
+ rgbx[i] = data[j];
175
+ rgbx[i + 1] = data[j + 1];
176
+ rgbx[i + 2] = data[j + 2];
177
+ rgbx[i + 3] = 255; // Alpha
178
+ }
179
+
180
+ display.blitImage(x, y, width, height, rgbx, 0, false);
169
181
 
170
182
  return true;
171
183
  }
@@ -195,6 +207,10 @@ export default class TightDecoder {
195
207
 
196
208
  let data;
197
209
 
210
+ if (uncompressedSize === 0) {
211
+ return true;
212
+ }
213
+
198
214
  if (uncompressedSize < 12) {
199
215
  if (sock.rQwait("TIGHT", uncompressedSize)) {
200
216
  return false;
@@ -237,7 +253,7 @@ export default class TightDecoder {
237
253
  for (let b = 7; b >= 0; b--) {
238
254
  dp = (y * width + x * 8 + 7 - b) * 4;
239
255
  sp = (data[y * w + x] >> b & 1) * 3;
240
- dest[dp] = palette[sp];
256
+ dest[dp] = palette[sp];
241
257
  dest[dp + 1] = palette[sp + 1];
242
258
  dest[dp + 2] = palette[sp + 2];
243
259
  dest[dp + 3] = 255;
@@ -247,14 +263,14 @@ export default class TightDecoder {
247
263
  for (let b = 7; b >= 8 - width % 8; b--) {
248
264
  dp = (y * width + x * 8 + 7 - b) * 4;
249
265
  sp = (data[y * w + x] >> b & 1) * 3;
250
- dest[dp] = palette[sp];
266
+ dest[dp] = palette[sp];
251
267
  dest[dp + 1] = palette[sp + 1];
252
268
  dest[dp + 2] = palette[sp + 2];
253
269
  dest[dp + 3] = 255;
254
270
  }
255
271
  }
256
272
 
257
- display.blitRgbxImage(x, y, width, height, dest, 0, false);
273
+ display.blitImage(x, y, width, height, dest, 0, false);
258
274
  }
259
275
 
260
276
  _paletteRect(x, y, width, height, data, palette, display) {
@@ -263,13 +279,13 @@ export default class TightDecoder {
263
279
  const total = width * height * 4;
264
280
  for (let i = 0, j = 0; i < total; i += 4, j++) {
265
281
  const sp = data[j] * 3;
266
- dest[i] = palette[sp];
282
+ dest[i] = palette[sp];
267
283
  dest[i + 1] = palette[sp + 1];
268
284
  dest[i + 2] = palette[sp + 2];
269
285
  dest[i + 3] = 255;
270
286
  }
271
287
 
272
- display.blitRgbxImage(x, y, width, height, dest, 0, false);
288
+ display.blitImage(x, y, width, height, dest, 0, false);
273
289
  }
274
290
 
275
291
  _gradientFilter(streamId, x, y, width, height, sock, display, depth) {
@@ -0,0 +1,185 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2021 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import Inflate from "../inflator.js";
11
+
12
+ const ZRLE_TILE_WIDTH = 64;
13
+ const ZRLE_TILE_HEIGHT = 64;
14
+
15
+ export default class ZRLEDecoder {
16
+ constructor() {
17
+ this._length = 0;
18
+ this._inflator = new Inflate();
19
+
20
+ this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
21
+ this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
22
+ }
23
+
24
+ decodeRect(x, y, width, height, sock, display, depth) {
25
+ if (this._length === 0) {
26
+ if (sock.rQwait("ZLib data length", 4)) {
27
+ return false;
28
+ }
29
+ this._length = sock.rQshift32();
30
+ }
31
+ if (sock.rQwait("Zlib data", this._length)) {
32
+ return false;
33
+ }
34
+
35
+ const data = sock.rQshiftBytes(this._length);
36
+
37
+ this._inflator.setInput(data);
38
+
39
+ for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {
40
+ let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);
41
+
42
+ for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {
43
+ let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);
44
+
45
+ const tileSize = tw * th;
46
+ const subencoding = this._inflator.inflate(1)[0];
47
+ if (subencoding === 0) {
48
+ // raw data
49
+ const data = this._readPixels(tileSize);
50
+ display.blitImage(tx, ty, tw, th, data, 0, false);
51
+ } else if (subencoding === 1) {
52
+ // solid
53
+ const background = this._readPixels(1);
54
+ display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);
55
+ } else if (subencoding >= 2 && subencoding <= 16) {
56
+ const data = this._decodePaletteTile(subencoding, tileSize, tw, th);
57
+ display.blitImage(tx, ty, tw, th, data, 0, false);
58
+ } else if (subencoding === 128) {
59
+ const data = this._decodeRLETile(tileSize);
60
+ display.blitImage(tx, ty, tw, th, data, 0, false);
61
+ } else if (subencoding >= 130 && subencoding <= 255) {
62
+ const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);
63
+ display.blitImage(tx, ty, tw, th, data, 0, false);
64
+ } else {
65
+ throw new Error('Unknown subencoding: ' + subencoding);
66
+ }
67
+ }
68
+ }
69
+ this._length = 0;
70
+ return true;
71
+ }
72
+
73
+ _getBitsPerPixelInPalette(paletteSize) {
74
+ if (paletteSize <= 2) {
75
+ return 1;
76
+ } else if (paletteSize <= 4) {
77
+ return 2;
78
+ } else if (paletteSize <= 16) {
79
+ return 4;
80
+ }
81
+ }
82
+
83
+ _readPixels(pixels) {
84
+ let data = this._pixelBuffer;
85
+ const buffer = this._inflator.inflate(3*pixels);
86
+ for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {
87
+ data[i] = buffer[j];
88
+ data[i + 1] = buffer[j + 1];
89
+ data[i + 2] = buffer[j + 2];
90
+ data[i + 3] = 255; // Add the Alpha
91
+ }
92
+ return data;
93
+ }
94
+
95
+ _decodePaletteTile(paletteSize, tileSize, tilew, tileh) {
96
+ const data = this._tileBuffer;
97
+ const palette = this._readPixels(paletteSize);
98
+ const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);
99
+ const mask = (1 << bitsPerPixel) - 1;
100
+
101
+ let offset = 0;
102
+ let encoded = this._inflator.inflate(1)[0];
103
+
104
+ for (let y=0; y<tileh; y++) {
105
+ let shift = 8-bitsPerPixel;
106
+ for (let x=0; x<tilew; x++) {
107
+ if (shift<0) {
108
+ shift=8-bitsPerPixel;
109
+ encoded = this._inflator.inflate(1)[0];
110
+ }
111
+ let indexInPalette = (encoded>>shift) & mask;
112
+
113
+ data[offset] = palette[indexInPalette * 4];
114
+ data[offset + 1] = palette[indexInPalette * 4 + 1];
115
+ data[offset + 2] = palette[indexInPalette * 4 + 2];
116
+ data[offset + 3] = palette[indexInPalette * 4 + 3];
117
+ offset += 4;
118
+ shift-=bitsPerPixel;
119
+ }
120
+ if (shift<8-bitsPerPixel && y<tileh-1) {
121
+ encoded = this._inflator.inflate(1)[0];
122
+ }
123
+ }
124
+ return data;
125
+ }
126
+
127
+ _decodeRLETile(tileSize) {
128
+ const data = this._tileBuffer;
129
+ let i = 0;
130
+ while (i < tileSize) {
131
+ const pixel = this._readPixels(1);
132
+ const length = this._readRLELength();
133
+ for (let j = 0; j < length; j++) {
134
+ data[i * 4] = pixel[0];
135
+ data[i * 4 + 1] = pixel[1];
136
+ data[i * 4 + 2] = pixel[2];
137
+ data[i * 4 + 3] = pixel[3];
138
+ i++;
139
+ }
140
+ }
141
+ return data;
142
+ }
143
+
144
+ _decodeRLEPaletteTile(paletteSize, tileSize) {
145
+ const data = this._tileBuffer;
146
+
147
+ // palette
148
+ const palette = this._readPixels(paletteSize);
149
+
150
+ let offset = 0;
151
+ while (offset < tileSize) {
152
+ let indexInPalette = this._inflator.inflate(1)[0];
153
+ let length = 1;
154
+ if (indexInPalette >= 128) {
155
+ indexInPalette -= 128;
156
+ length = this._readRLELength();
157
+ }
158
+ if (indexInPalette > paletteSize) {
159
+ throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);
160
+ }
161
+ if (offset + length > tileSize) {
162
+ throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));
163
+ }
164
+
165
+ for (let j = 0; j < length; j++) {
166
+ data[offset * 4] = palette[indexInPalette * 4];
167
+ data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];
168
+ data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];
169
+ data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];
170
+ offset++;
171
+ }
172
+ }
173
+ return data;
174
+ }
175
+
176
+ _readRLELength() {
177
+ let length = 0;
178
+ let current = 0;
179
+ do {
180
+ current = this._inflator.inflate(1)[0];
181
+ length += current;
182
+ } while (current === 255);
183
+ return length + 1;
184
+ }
185
+ }