mirror of
https://github.com/novnc/noVNC.git
synced 2026-06-03 10:59:39 +00:00
This is what the browser wants so it avoids having to spend time converting everything. Unfortunately it usually means the server instead needs to convert it for us, but we assume it has more power than we do.
192 lines
6.2 KiB
JavaScript
192 lines
6.2 KiB
JavaScript
/*
|
|
* noVNC: HTML5 VNC client
|
|
* Copyright (C) 2019 The noVNC Authors
|
|
* 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;
|
|
this._tileBuffer = new Uint8Array(16 * 16 * 4);
|
|
}
|
|
|
|
decodeRect(x, y, width, height, sock, display, depth) {
|
|
if (this._tiles === 0) {
|
|
this._tilesX = Math.ceil(width / 16);
|
|
this._tilesY = Math.ceil(height / 16);
|
|
this._totalTiles = this._tilesX * this._tilesY;
|
|
this._tiles = this._totalTiles;
|
|
}
|
|
|
|
while (this._tiles > 0) {
|
|
let bytes = 1;
|
|
|
|
if (sock.rQwait("HEXTILE", bytes)) {
|
|
return false;
|
|
}
|
|
|
|
let rQ = sock.rQ;
|
|
let rQi = sock.rQi;
|
|
|
|
let subencoding = rQ[rQi]; // Peek
|
|
if (subencoding > 30) { // Raw
|
|
throw new Error("Illegal hextile subencoding (subencoding: " +
|
|
subencoding + ")");
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
let subrects = rQ[rQi + bytes - 1]; // Peek
|
|
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
|
|
rQi++;
|
|
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
|
|
let pixels = tw * th;
|
|
// Max sure the image is fully opaque
|
|
for (let i = 0;i < pixels;i++) {
|
|
rQ[rQi + i * 4 + 3] = 255;
|
|
}
|
|
display.blitImage(tx, ty, tw, th, rQ, rQi);
|
|
rQi += bytes - 1;
|
|
} else {
|
|
if (subencoding & 0x02) { // Background
|
|
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
|
rQi += 4;
|
|
}
|
|
if (subencoding & 0x04) { // Foreground
|
|
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
|
rQi += 4;
|
|
}
|
|
|
|
this._startTile(tx, ty, tw, th, this._background);
|
|
if (subencoding & 0x08) { // AnySubrects
|
|
let subrects = rQ[rQi];
|
|
rQi++;
|
|
|
|
for (let s = 0; s < subrects; s++) {
|
|
let color;
|
|
if (subencoding & 0x10) { // SubrectsColoured
|
|
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
|
rQi += 4;
|
|
} else {
|
|
color = this._foreground;
|
|
}
|
|
const xy = rQ[rQi];
|
|
rQi++;
|
|
const sx = (xy >> 4);
|
|
const sy = (xy & 0x0f);
|
|
|
|
const wh = rQ[rQi];
|
|
rQi++;
|
|
const sw = (wh >> 4) + 1;
|
|
const sh = (wh & 0x0f) + 1;
|
|
|
|
this._subTile(sx, sy, sw, sh, color);
|
|
}
|
|
}
|
|
this._finishTile(display);
|
|
}
|
|
sock.rQi = rQi;
|
|
this._lastsubencoding = subencoding;
|
|
this._tiles--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// start updating a tile
|
|
_startTile(x, y, width, height, color) {
|
|
this._tileX = x;
|
|
this._tileY = y;
|
|
this._tileW = width;
|
|
this._tileH = height;
|
|
|
|
const red = color[0];
|
|
const green = color[1];
|
|
const blue = color[2];
|
|
|
|
const data = this._tileBuffer;
|
|
for (let i = 0; i < width * height * 4; i += 4) {
|
|
data[i] = red;
|
|
data[i + 1] = green;
|
|
data[i + 2] = blue;
|
|
data[i + 3] = 255;
|
|
}
|
|
}
|
|
|
|
// update sub-rectangle of the current tile
|
|
_subTile(x, y, w, h, color) {
|
|
const red = color[0];
|
|
const green = color[1];
|
|
const blue = color[2];
|
|
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;
|
|
data[p] = red;
|
|
data[p + 1] = green;
|
|
data[p + 2] = blue;
|
|
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);
|
|
}
|
|
}
|