SPB Git forge

spb/admin-ka

Public
41commits 1branches 0releases
172.9 MBsize
maindefault branch
20 days agolast push
JavaScript 65.5% Python 17.8% CSS 13% HTML 3.7%
11.7 KB · 394 lines javascript
Raw Blame History
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