]> git.proxmox.com Git - mirror_novnc.git/blob - core/decoders/hextile.js
Move tile handling to Hextile decoder
[mirror_novnc.git] / core / decoders / hextile.js
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 import * as Log from '../util/logging.js';
11
12 export default class HextileDecoder {
13 constructor() {
14 this._tiles = 0;
15 this._lastsubencoding = 0;
16 this._tileBuffer = new Uint8Array(16 * 16 * 4);
17 }
18
19 decodeRect(x, y, width, height, sock, display, depth) {
20 if (this._tiles === 0) {
21 this._tilesX = Math.ceil(width / 16);
22 this._tilesY = Math.ceil(height / 16);
23 this._totalTiles = this._tilesX * this._tilesY;
24 this._tiles = this._totalTiles;
25 }
26
27 while (this._tiles > 0) {
28 let bytes = 1;
29
30 if (sock.rQwait("HEXTILE", bytes)) {
31 return false;
32 }
33
34 let rQ = sock.rQ;
35 let rQi = sock.rQi;
36
37 let subencoding = rQ[rQi]; // Peek
38 if (subencoding > 30) { // Raw
39 throw new Error("Illegal hextile subencoding (subencoding: " +
40 subencoding + ")");
41 }
42
43 const currTile = this._totalTiles - this._tiles;
44 const tileX = currTile % this._tilesX;
45 const tileY = Math.floor(currTile / this._tilesX);
46 const tx = x + tileX * 16;
47 const ty = y + tileY * 16;
48 const tw = Math.min(16, (x + width) - tx);
49 const th = Math.min(16, (y + height) - ty);
50
51 // Figure out how much we are expecting
52 if (subencoding & 0x01) { // Raw
53 bytes += tw * th * 4;
54 } else {
55 if (subencoding & 0x02) { // Background
56 bytes += 4;
57 }
58 if (subencoding & 0x04) { // Foreground
59 bytes += 4;
60 }
61 if (subencoding & 0x08) { // AnySubrects
62 bytes++; // Since we aren't shifting it off
63
64 if (sock.rQwait("HEXTILE", bytes)) {
65 return false;
66 }
67
68 let subrects = rQ[rQi + bytes - 1]; // Peek
69 if (subencoding & 0x10) { // SubrectsColoured
70 bytes += subrects * (4 + 2);
71 } else {
72 bytes += subrects * 2;
73 }
74 }
75 }
76
77 if (sock.rQwait("HEXTILE", bytes)) {
78 return false;
79 }
80
81 // We know the encoding and have a whole tile
82 rQi++;
83 if (subencoding === 0) {
84 if (this._lastsubencoding & 0x01) {
85 // Weird: ignore blanks are RAW
86 Log.Debug(" Ignoring blank after RAW");
87 } else {
88 display.fillRect(tx, ty, tw, th, this._background);
89 }
90 } else if (subencoding & 0x01) { // Raw
91 display.blitImage(tx, ty, tw, th, rQ, rQi);
92 rQi += bytes - 1;
93 } else {
94 if (subencoding & 0x02) { // Background
95 this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
96 rQi += 4;
97 }
98 if (subencoding & 0x04) { // Foreground
99 this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
100 rQi += 4;
101 }
102
103 this._startTile(tx, ty, tw, th, this._background);
104 if (subencoding & 0x08) { // AnySubrects
105 let subrects = rQ[rQi];
106 rQi++;
107
108 for (let s = 0; s < subrects; s++) {
109 let color;
110 if (subencoding & 0x10) { // SubrectsColoured
111 color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
112 rQi += 4;
113 } else {
114 color = this._foreground;
115 }
116 const xy = rQ[rQi];
117 rQi++;
118 const sx = (xy >> 4);
119 const sy = (xy & 0x0f);
120
121 const wh = rQ[rQi];
122 rQi++;
123 const sw = (wh >> 4) + 1;
124 const sh = (wh & 0x0f) + 1;
125
126 this._subTile(sx, sy, sw, sh, color);
127 }
128 }
129 this._finishTile(display);
130 }
131 sock.rQi = rQi;
132 this._lastsubencoding = subencoding;
133 this._tiles--;
134 }
135
136 return true;
137 }
138
139 // start updating a tile
140 _startTile(x, y, width, height, color) {
141 this._tileX = x;
142 this._tileY = y;
143 this._tileW = width;
144 this._tileH = height;
145
146 const red = color[2];
147 const green = color[1];
148 const blue = color[0];
149
150 const data = this._tileBuffer;
151 for (let i = 0; i < width * height * 4; i += 4) {
152 data[i] = blue;
153 data[i + 1] = green;
154 data[i + 2] = red;
155 data[i + 3] = 255;
156 }
157 }
158
159 // update sub-rectangle of the current tile
160 _subTile(x, y, w, h, color) {
161 const red = color[2];
162 const green = color[1];
163 const blue = color[0];
164 const xend = x + w;
165 const yend = y + h;
166
167 const data = this._tileBuffer;
168 const width = this._tileW;
169 for (let j = y; j < yend; j++) {
170 for (let i = x; i < xend; i++) {
171 const p = (i + (j * width)) * 4;
172 data[p] = blue;
173 data[p + 1] = green;
174 data[p + 2] = red;
175 data[p + 3] = 255;
176 }
177 }
178 }
179
180 // draw the current tile to the screen
181 _finishTile(display) {
182 display.blitImage(this._tileX, this._tileY,
183 this._tileW, this._tileH,
184 this._tileBuffer, 0);
185 }
186 }