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);
|
|
}
|
|
}
|