Files
noVNC/core/decoders/hextile.js
T

182 lines
5.9 KiB
JavaScript
Raw Normal View History

2018-07-20 16:05:16 +02:00
/*
* noVNC: HTML5 VNC client
2024-11-27 13:58:26 +01:00
* Copyright (C) 2019 The noVNC authors
2018-07-20 16:05:16 +02:00
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
export default class HextileDecoder {
constructor() {
this._tiles = 0;
this._lastsubencoding = 0;
2018-10-12 17:07:16 +02:00
this._tileBuffer = new Uint8Array(16 * 16 * 4);
2018-07-20 16:05:16 +02:00
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._tiles === 0) {
2020-05-31 23:27:58 +02:00
this._tilesX = Math.ceil(width / 16);
this._tilesY = Math.ceil(height / 16);
this._totalTiles = this._tilesX * this._tilesY;
this._tiles = this._totalTiles;
2018-07-20 16:05:16 +02:00
}
while (this._tiles > 0) {
let bytes = 1;
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
2023-05-11 23:06:34 +02:00
let subencoding = sock.rQpeek8();
2018-07-20 16:05:16 +02:00
if (subencoding > 30) { // Raw
2018-11-24 21:44:11 +02:00
throw new Error("Illegal hextile subencoding (subencoding: " +
2018-07-20 16:05:16 +02:00
subencoding + ")");
}
2020-05-31 23:27:58 +02:00
const currTile = this._totalTiles - this._tiles;
const tileX = currTile % this._tilesX;
const tileY = Math.floor(currTile / this._tilesX);
const tx = x + tileX * 16;
const ty = y + tileY * 16;
2018-07-20 16:05:16 +02:00
const tw = Math.min(16, (x + width) - tx);
const th = Math.min(16, (y + height) - ty);
// Figure out how much we are expecting
if (subencoding & 0x01) { // Raw
bytes += tw * th * 4;
} else {
if (subencoding & 0x02) { // Background
bytes += 4;
}
if (subencoding & 0x04) { // Foreground
bytes += 4;
}
if (subencoding & 0x08) { // AnySubrects
bytes++; // Since we aren't shifting it off
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
2023-05-11 23:06:34 +02:00
let subrects = sock.rQpeekBytes(bytes).at(-1);
2018-07-20 16:05:16 +02:00
if (subencoding & 0x10) { // SubrectsColoured
bytes += subrects * (4 + 2);
} else {
bytes += subrects * 2;
}
}
}
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
// We know the encoding and have a whole tile
2023-05-11 23:06:34 +02:00
sock.rQshift8();
2018-07-20 16:05:16 +02:00
if (subencoding === 0) {
if (this._lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW
Log.Debug(" Ignoring blank after RAW");
} else {
display.fillRect(tx, ty, tw, th, this._background);
}
} else if (subencoding & 0x01) { // Raw
2020-06-07 14:22:07 +02:00
let pixels = tw * th;
2023-05-16 19:06:10 +02:00
let data = sock.rQshiftBytes(pixels * 4, false);
2020-06-07 14:22:07 +02:00
// Max sure the image is fully opaque
for (let i = 0;i < pixels;i++) {
2023-05-11 23:06:34 +02:00
data[i * 4 + 3] = 255;
2020-06-07 14:22:07 +02:00
}
2023-05-11 23:06:34 +02:00
display.blitImage(tx, ty, tw, th, data, 0);
2018-07-20 16:05:16 +02:00
} else {
if (subencoding & 0x02) { // Background
2023-05-11 23:06:34 +02:00
this._background = new Uint8Array(sock.rQshiftBytes(4));
2018-07-20 16:05:16 +02:00
}
if (subencoding & 0x04) { // Foreground
2023-05-11 23:06:34 +02:00
this._foreground = new Uint8Array(sock.rQshiftBytes(4));
2018-07-20 16:05:16 +02:00
}
2018-10-12 17:07:16 +02:00
this._startTile(tx, ty, tw, th, this._background);
2018-07-20 16:05:16 +02:00
if (subencoding & 0x08) { // AnySubrects
2023-05-11 23:06:34 +02:00
let subrects = sock.rQshift8();
2018-07-20 16:05:16 +02:00
for (let s = 0; s < subrects; s++) {
let color;
if (subencoding & 0x10) { // SubrectsColoured
2023-05-11 23:06:34 +02:00
color = sock.rQshiftBytes(4);
2018-07-20 16:05:16 +02:00
} else {
color = this._foreground;
}
2023-05-11 23:06:34 +02:00
const xy = sock.rQshift8();
2018-07-20 16:05:16 +02:00
const sx = (xy >> 4);
const sy = (xy & 0x0f);
2023-05-11 23:06:34 +02:00
const wh = sock.rQshift8();
2018-07-20 16:05:16 +02:00
const sw = (wh >> 4) + 1;
const sh = (wh & 0x0f) + 1;
2018-10-12 17:07:16 +02:00
this._subTile(sx, sy, sw, sh, color);
2018-07-20 16:05:16 +02:00
}
}
2018-10-12 17:07:16 +02:00
this._finishTile(display);
2018-07-20 16:05:16 +02:00
}
this._lastsubencoding = subencoding;
this._tiles--;
}
return true;
}
2018-10-12 17:07:16 +02:00
// start updating a tile
_startTile(x, y, width, height, color) {
this._tileX = x;
this._tileY = y;
this._tileW = width;
this._tileH = height;
2020-06-07 14:22:07 +02:00
const red = color[0];
2018-10-12 17:07:16 +02:00
const green = color[1];
2020-06-07 14:22:07 +02:00
const blue = color[2];
2018-10-12 17:07:16 +02:00
const data = this._tileBuffer;
for (let i = 0; i < width * height * 4; i += 4) {
2020-06-07 14:22:07 +02:00
data[i] = red;
2018-10-12 17:07:16 +02:00
data[i + 1] = green;
2020-06-07 14:22:07 +02:00
data[i + 2] = blue;
2018-10-12 17:07:16 +02:00
data[i + 3] = 255;
}
}
// update sub-rectangle of the current tile
_subTile(x, y, w, h, color) {
2020-06-07 14:22:07 +02:00
const red = color[0];
2018-10-12 17:07:16 +02:00
const green = color[1];
2020-06-07 14:22:07 +02:00
const blue = color[2];
2018-10-12 17:07:16 +02:00
const xend = x + w;
const yend = y + h;
const data = this._tileBuffer;
const width = this._tileW;
for (let j = y; j < yend; j++) {
for (let i = x; i < xend; i++) {
const p = (i + (j * width)) * 4;
2020-06-07 14:22:07 +02:00
data[p] = red;
2018-10-12 17:07:16 +02:00
data[p + 1] = green;
2020-06-07 14:22:07 +02:00
data[p + 2] = blue;
2018-10-12 17:07:16 +02:00
data[p + 3] = 255;
}
}
}
// draw the current tile to the screen
_finishTile(display) {
display.blitImage(this._tileX, this._tileY,
this._tileW, this._tileH,
this._tileBuffer, 0);
}
2018-07-20 16:05:16 +02:00
}