JavaScript 65.5%
Python 17.8%
CSS 13%
HTML 3.7%
1/*2 * noVNC: HTML5 VNC client3 * Copyright (C) 2019 The noVNC authors4 * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)5 * Licensed under MPL 2.0 (see LICENSE.txt)6 *7 * See README.md for usage and integration instructions.8 *9 */1011import * as Log from '../util/logging.js';12import Inflator from "../inflator.js";1314export default class TightDecoder {15 constructor() {16 this._ctl = null;17 this._filter = null;18 this._numColors = 0;19 this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)20 this._len = 0;2122 this._zlibs = [];23 for (let i = 0; i < 4; i++) {24 this._zlibs[i] = new Inflator();25 }26 }2728 decodeRect(x, y, width, height, sock, display, depth) {29 if (this._ctl === null) {30 if (sock.rQwait("TIGHT compression-control", 1)) {31 return false;32 }3334 this._ctl = sock.rQshift8();3536 // Reset streams if the server requests it37 for (let i = 0; i < 4; i++) {38 if ((this._ctl >> i) & 1) {39 this._zlibs[i].reset();40 Log.Info("Reset zlib stream " + i);41 }42 }4344 // Figure out filter45 this._ctl = this._ctl >> 4;46 }4748 let ret;4950 if (this._ctl === 0x08) {51 ret = this._fillRect(x, y, width, height,52 sock, display, depth);53 } else if (this._ctl === 0x09) {54 ret = this._jpegRect(x, y, width, height,55 sock, display, depth);56 } else if (this._ctl === 0x0A) {57 ret = this._pngRect(x, y, width, height,58 sock, display, depth);59 } else if ((this._ctl & 0x08) == 0) {60 ret = this._basicRect(this._ctl, x, y, width, height,61 sock, display, depth);62 } else {63 throw new Error("Illegal tight compression received (ctl: " +64 this._ctl + ")");65 }6667 if (ret) {68 this._ctl = null;69 }7071 return ret;72 }7374 _fillRect(x, y, width, height, sock, display, depth) {75 if (sock.rQwait("TIGHT", 3)) {76 return false;77 }7879 let pixel = sock.rQshiftBytes(3);80 display.fillRect(x, y, width, height, pixel, false);8182 return true;83 }8485 _jpegRect(x, y, width, height, sock, display, depth) {86 let data = this._readData(sock);87 if (data === null) {88 return false;89 }9091 display.imageRect(x, y, width, height, "image/jpeg", data);9293 return true;94 }9596 _pngRect(x, y, width, height, sock, display, depth) {97 throw new Error("PNG received in standard Tight rect");98 }99100 _basicRect(ctl, x, y, width, height, sock, display, depth) {101 if (this._filter === null) {102 if (ctl & 0x4) {103 if (sock.rQwait("TIGHT", 1)) {104 return false;105 }106107 this._filter = sock.rQshift8();108 } else {109 // Implicit CopyFilter110 this._filter = 0;111 }112 }113114 let streamId = ctl & 0x3;115116 let ret;117118 switch (this._filter) {119 case 0: // CopyFilter120 ret = this._copyFilter(streamId, x, y, width, height,121 sock, display, depth);122 break;123 case 1: // PaletteFilter124 ret = this._paletteFilter(streamId, x, y, width, height,125 sock, display, depth);126 break;127 case 2: // GradientFilter128 ret = this._gradientFilter(streamId, x, y, width, height,129 sock, display, depth);130 break;131 default:132 throw new Error("Illegal tight filter received (ctl: " +133 this._filter + ")");134 }135136 if (ret) {137 this._filter = null;138 }139140 return ret;141 }142143 _copyFilter(streamId, x, y, width, height, sock, display, depth) {144 const uncompressedSize = width * height * 3;145 let data;146147 if (uncompressedSize === 0) {148 return true;149 }150151 if (uncompressedSize < 12) {152 if (sock.rQwait("TIGHT", uncompressedSize)) {153 return false;154 }155156 data = sock.rQshiftBytes(uncompressedSize);157 } else {158 data = this._readData(sock);159 if (data === null) {160 return false;161 }162163 this._zlibs[streamId].setInput(data);164 data = this._zlibs[streamId].inflate(uncompressedSize);165 this._zlibs[streamId].setInput(null);166 }167168 let rgbx = new Uint8Array(width * height * 4);169 for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {170 rgbx[i] = data[j];171 rgbx[i + 1] = data[j + 1];172 rgbx[i + 2] = data[j + 2];173 rgbx[i + 3] = 255; // Alpha174 }175176 display.blitImage(x, y, width, height, rgbx, 0, false);177178 return true;179 }180181 _paletteFilter(streamId, x, y, width, height, sock, display, depth) {182 if (this._numColors === 0) {183 if (sock.rQwait("TIGHT palette", 1)) {184 return false;185 }186187 const numColors = sock.rQpeek8() + 1;188 const paletteSize = numColors * 3;189190 if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {191 return false;192 }193194 this._numColors = numColors;195 sock.rQskipBytes(1);196197 sock.rQshiftTo(this._palette, paletteSize);198 }199200 const bpp = (this._numColors <= 2) ? 1 : 8;201 const rowSize = Math.floor((width * bpp + 7) / 8);202 const uncompressedSize = rowSize * height;203204 let data;205206 if (uncompressedSize === 0) {207 return true;208 }209210 if (uncompressedSize < 12) {211 if (sock.rQwait("TIGHT", uncompressedSize)) {212 return false;213 }214215 data = sock.rQshiftBytes(uncompressedSize);216 } else {217 data = this._readData(sock);218 if (data === null) {219 return false;220 }221222 this._zlibs[streamId].setInput(data);223 data = this._zlibs[streamId].inflate(uncompressedSize);224 this._zlibs[streamId].setInput(null);225 }226227 // Convert indexed (palette based) image data to RGB228 if (this._numColors == 2) {229 this._monoRect(x, y, width, height, data, this._palette, display);230 } else {231 this._paletteRect(x, y, width, height, data, this._palette, display);232 }233234 this._numColors = 0;235236 return true;237 }238239 _monoRect(x, y, width, height, data, palette, display) {240 // Convert indexed (palette based) image data to RGB241 // TODO: reduce number of calculations inside loop242 const dest = this._getScratchBuffer(width * height * 4);243 const w = Math.floor((width + 7) / 8);244 const w1 = Math.floor(width / 8);245246 for (let y = 0; y < height; y++) {247 let dp, sp, x;248 for (x = 0; x < w1; x++) {249 for (let b = 7; b >= 0; b--) {250 dp = (y * width + x * 8 + 7 - b) * 4;251 sp = (data[y * w + x] >> b & 1) * 3;252 dest[dp] = palette[sp];253 dest[dp + 1] = palette[sp + 1];254 dest[dp + 2] = palette[sp + 2];255 dest[dp + 3] = 255;256 }257 }258259 for (let b = 7; b >= 8 - width % 8; b--) {260 dp = (y * width + x * 8 + 7 - b) * 4;261 sp = (data[y * w + x] >> b & 1) * 3;262 dest[dp] = palette[sp];263 dest[dp + 1] = palette[sp + 1];264 dest[dp + 2] = palette[sp + 2];265 dest[dp + 3] = 255;266 }267 }268269 display.blitImage(x, y, width, height, dest, 0, false);270 }271272 _paletteRect(x, y, width, height, data, palette, display) {273 // Convert indexed (palette based) image data to RGB274 const dest = this._getScratchBuffer(width * height * 4);275 const total = width * height * 4;276 for (let i = 0, j = 0; i < total; i += 4, j++) {277 const sp = data[j] * 3;278 dest[i] = palette[sp];279 dest[i + 1] = palette[sp + 1];280 dest[i + 2] = palette[sp + 2];281 dest[i + 3] = 255;282 }283284 display.blitImage(x, y, width, height, dest, 0, false);285 }286287 _gradientFilter(streamId, x, y, width, height, sock, display, depth) {288 // assume the TPIXEL is 3 bytes long289 const uncompressedSize = width * height * 3;290 let data;291292 if (uncompressedSize === 0) {293 return true;294 }295296 if (uncompressedSize < 12) {297 if (sock.rQwait("TIGHT", uncompressedSize)) {298 return false;299 }300301 data = sock.rQshiftBytes(uncompressedSize);302 } else {303 data = this._readData(sock);304 if (data === null) {305 return false;306 }307308 this._zlibs[streamId].setInput(data);309 data = this._zlibs[streamId].inflate(uncompressedSize);310 this._zlibs[streamId].setInput(null);311 }312313 let rgbx = new Uint8Array(4 * width * height);314315 let rgbxIndex = 0, dataIndex = 0;316 let left = new Uint8Array(3);317 for (let x = 0; x < width; x++) {318 for (let c = 0; c < 3; c++) {319 const prediction = left[c];320 const value = data[dataIndex++] + prediction;321 rgbx[rgbxIndex++] = value;322 left[c] = value;323 }324 rgbx[rgbxIndex++] = 255;325 }326327 let upperIndex = 0;328 let upper = new Uint8Array(3),329 upperleft = new Uint8Array(3);330 for (let y = 1; y < height; y++) {331 left.fill(0);332 upperleft.fill(0);333 for (let x = 0; x < width; x++) {334 for (let c = 0; c < 3; c++) {335 upper[c] = rgbx[upperIndex++];336 let prediction = left[c] + upper[c] - upperleft[c];337 if (prediction < 0) {338 prediction = 0;339 } else if (prediction > 255) {340 prediction = 255;341 }342 const value = data[dataIndex++] + prediction;343 rgbx[rgbxIndex++] = value;344 upperleft[c] = upper[c];345 left[c] = value;346 }347 rgbx[rgbxIndex++] = 255;348 upperIndex++;349 }350 }351352 display.blitImage(x, y, width, height, rgbx, 0, false);353354 return true;355 }356357 _readData(sock) {358 if (this._len === 0) {359 if (sock.rQwait("TIGHT", 3)) {360 return null;361 }362363 let byte;364365 byte = sock.rQshift8();366 this._len = byte & 0x7f;367 if (byte & 0x80) {368 byte = sock.rQshift8();369 this._len |= (byte & 0x7f) << 7;370 if (byte & 0x80) {371 byte = sock.rQshift8();372 this._len |= byte << 14;373 }374 }375 }376377 if (sock.rQwait("TIGHT", this._len)) {378 return null;379 }380381 let data = sock.rQshiftBytes(this._len, false);382 this._len = 0;383384 return data;385 }386387 _getScratchBuffer(size) {388 if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {389 this._scratchBuffer = new Uint8Array(size);390 }391 return this._scratchBuffer;392 }393}394