2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2019 The noVNC Authors
4 * Licensed under MPL 2.0 (see LICENSE.txt)
6 * See README.md for usage and integration instructions.
10import * as Log from '../util/logging.js';
12export default class HextileDecoder {
15 this._lastsubencoding = 0;
16 this._tileBuffer = new Uint8Array(16 * 16 * 4);
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;
27 while (this._tiles > 0) {
30 if (sock.rQwait("HEXTILE", bytes)) {
34 let subencoding = sock.rQpeek8();
35 if (subencoding > 30) { // Raw
36 throw new Error("Illegal hextile subencoding (subencoding: " +
40 const currTile = this._totalTiles - this._tiles;
41 const tileX = currTile % this._tilesX;
42 const tileY = Math.floor(currTile / this._tilesX);
43 const tx = x + tileX * 16;
44 const ty = y + tileY * 16;
45 const tw = Math.min(16, (x + width) - tx);
46 const th = Math.min(16, (y + height) - ty);
48 // Figure out how much we are expecting
49 if (subencoding & 0x01) { // Raw
52 if (subencoding & 0x02) { // Background
55 if (subencoding & 0x04) { // Foreground
58 if (subencoding & 0x08) { // AnySubrects
59 bytes++; // Since we aren't shifting it off
61 if (sock.rQwait("HEXTILE", bytes)) {
65 let subrects = sock.rQpeekBytes(bytes).at(-1);
66 if (subencoding & 0x10) { // SubrectsColoured
67 bytes += subrects * (4 + 2);
69 bytes += subrects * 2;
74 if (sock.rQwait("HEXTILE", bytes)) {
78 // We know the encoding and have a whole tile
80 if (subencoding === 0) {
81 if (this._lastsubencoding & 0x01) {
82 // Weird: ignore blanks are RAW
83 Log.Debug(" Ignoring blank after RAW");
85 display.fillRect(tx, ty, tw, th, this._background);
87 } else if (subencoding & 0x01) { // Raw
89 let data = sock.rQshiftBytes(pixels * 4, false);
90 // Max sure the image is fully opaque
91 for (let i = 0;i < pixels;i++) {
92 data[i * 4 + 3] = 255;
94 display.blitImage(tx, ty, tw, th, data, 0);
96 if (subencoding & 0x02) { // Background
97 this._background = new Uint8Array(sock.rQshiftBytes(4));
99 if (subencoding & 0x04) { // Foreground
100 this._foreground = new Uint8Array(sock.rQshiftBytes(4));
103 this._startTile(tx, ty, tw, th, this._background);
104 if (subencoding & 0x08) { // AnySubrects
105 let subrects = sock.rQshift8();
107 for (let s = 0; s < subrects; s++) {
109 if (subencoding & 0x10) { // SubrectsColoured
110 color = sock.rQshiftBytes(4);
112 color = this._foreground;
114 const xy = sock.rQshift8();
115 const sx = (xy >> 4);
116 const sy = (xy & 0x0f);
118 const wh = sock.rQshift8();
119 const sw = (wh >> 4) + 1;
120 const sh = (wh & 0x0f) + 1;
122 this._subTile(sx, sy, sw, sh, color);
125 this._finishTile(display);
127 this._lastsubencoding = subencoding;
134 // start updating a tile
135 _startTile(x, y, width, height, color) {
139 this._tileH = height;
141 const red = color[0];
142 const green = color[1];
143 const blue = color[2];
145 const data = this._tileBuffer;
146 for (let i = 0; i < width * height * 4; i += 4) {
154 // update sub-rectangle of the current tile
155 _subTile(x, y, w, h, color) {
156 const red = color[0];
157 const green = color[1];
158 const blue = color[2];
162 const data = this._tileBuffer;
163 const width = this._tileW;
164 for (let j = y; j < yend; j++) {
165 for (let i = x; i < xend; i++) {
166 const p = (i + (j * width)) * 4;
175 // draw the current tile to the screen
176 _finishTile(display) {
177 display.blitImage(this._tileX, this._tileY,
178 this._tileW, this._tileH,
179 this._tileBuffer, 0);