mirror of
https://git.proxmox.com/git/mirror_novnc
synced 2025-04-29 12:15:32 +00:00

Try to be more consistent in how we capitalize things. Both the "Title Case" and "Sentence case" styles are popular, so either would work. Google and Mozilla both prefer "Sentence case", so let's follow them.
147 lines
4.1 KiB
JavaScript
147 lines
4.1 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.
|
|
*
|
|
*/
|
|
|
|
export default class JPEGDecoder {
|
|
constructor() {
|
|
// RealVNC will reuse the quantization tables
|
|
// and Huffman tables, so we need to cache them.
|
|
this._cachedQuantTables = [];
|
|
this._cachedHuffmanTables = [];
|
|
|
|
this._segments = [];
|
|
}
|
|
|
|
decodeRect(x, y, width, height, sock, display, depth) {
|
|
// A rect of JPEG encodings is simply a JPEG file
|
|
while (true) {
|
|
let segment = this._readSegment(sock);
|
|
if (segment === null) {
|
|
return false;
|
|
}
|
|
this._segments.push(segment);
|
|
// End of image?
|
|
if (segment[1] === 0xD9) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
let huffmanTables = [];
|
|
let quantTables = [];
|
|
for (let segment of this._segments) {
|
|
let type = segment[1];
|
|
if (type === 0xC4) {
|
|
// Huffman tables
|
|
huffmanTables.push(segment);
|
|
} else if (type === 0xDB) {
|
|
// Quantization tables
|
|
quantTables.push(segment);
|
|
}
|
|
}
|
|
|
|
const sofIndex = this._segments.findIndex(
|
|
x => x[1] == 0xC0 || x[1] == 0xC2
|
|
);
|
|
if (sofIndex == -1) {
|
|
throw new Error("Illegal JPEG image without SOF");
|
|
}
|
|
|
|
if (quantTables.length === 0) {
|
|
this._segments.splice(sofIndex+1, 0,
|
|
...this._cachedQuantTables);
|
|
}
|
|
if (huffmanTables.length === 0) {
|
|
this._segments.splice(sofIndex+1, 0,
|
|
...this._cachedHuffmanTables);
|
|
}
|
|
|
|
let length = 0;
|
|
for (let segment of this._segments) {
|
|
length += segment.length;
|
|
}
|
|
|
|
let data = new Uint8Array(length);
|
|
length = 0;
|
|
for (let segment of this._segments) {
|
|
data.set(segment, length);
|
|
length += segment.length;
|
|
}
|
|
|
|
display.imageRect(x, y, width, height, "image/jpeg", data);
|
|
|
|
if (huffmanTables.length !== 0) {
|
|
this._cachedHuffmanTables = huffmanTables;
|
|
}
|
|
if (quantTables.length !== 0) {
|
|
this._cachedQuantTables = quantTables;
|
|
}
|
|
|
|
this._segments = [];
|
|
|
|
return true;
|
|
}
|
|
|
|
_readSegment(sock) {
|
|
if (sock.rQwait("JPEG", 2)) {
|
|
return null;
|
|
}
|
|
|
|
let marker = sock.rQshift8();
|
|
if (marker != 0xFF) {
|
|
throw new Error("Illegal JPEG marker received (byte: " +
|
|
marker + ")");
|
|
}
|
|
let type = sock.rQshift8();
|
|
if (type >= 0xD0 && type <= 0xD9 || type == 0x01) {
|
|
// No length after marker
|
|
return new Uint8Array([marker, type]);
|
|
}
|
|
|
|
if (sock.rQwait("JPEG", 2, 2)) {
|
|
return null;
|
|
}
|
|
|
|
let length = sock.rQshift16();
|
|
if (length < 2) {
|
|
throw new Error("Illegal JPEG length received (length: " +
|
|
length + ")");
|
|
}
|
|
|
|
if (sock.rQwait("JPEG", length-2, 4)) {
|
|
return null;
|
|
}
|
|
|
|
let extra = 0;
|
|
if (type === 0xDA) {
|
|
// start of scan
|
|
extra += 2;
|
|
while (true) {
|
|
if (sock.rQwait("JPEG", length-2+extra, 4)) {
|
|
return null;
|
|
}
|
|
let data = sock.rQpeekBytes(length-2+extra, false);
|
|
if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
|
|
!(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
|
|
extra -= 2;
|
|
break;
|
|
}
|
|
extra++;
|
|
}
|
|
}
|
|
|
|
let segment = new Uint8Array(2 + length + extra);
|
|
segment[0] = marker;
|
|
segment[1] = type;
|
|
segment[2] = length >> 8;
|
|
segment[3] = length;
|
|
segment.set(sock.rQshiftBytes(length-2+extra, false), 4);
|
|
|
|
return segment;
|
|
}
|
|
}
|