summaryrefslogtreecommitdiff
path: root/core/decoders/hextile.js
blob: 91141aa4b39aa9db11bae5968c6fda3153573a72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * noVNC: HTML5 VNC client
 * Copyright (C) 2012 Joel Martin
 * Copyright (C) 2018 Samuel Mannehed for Cendio AB
 * Copyright (C) 2018 Pierre Ossman for Cendio AB
 * 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;
    }

    decodeRect(x, y, width, height, sock, display, depth) {
        if (this._tiles === 0) {
            this._tiles_x = Math.ceil(width / 16);
            this._tiles_y = Math.ceil(height / 16);
            this._total_tiles = this._tiles_x * this._tiles_y;
            this._tiles = this._total_tiles;
        }

        while (this._tiles > 0) {
            let bytes = 1;

            if (sock.rQwait("HEXTILE", bytes)) {
                return false;
            }

            let rQ = sock.get_rQ();
            let rQi = sock.get_rQi();

            let subencoding = rQ[rQi];  // Peek
            if (subencoding > 30) {  // Raw
                throw Error("Illegal hextile subencoding (subencoding: " +
                            subencoding + ")");
            }

            const curr_tile = this._total_tiles - this._tiles;
            const tile_x = curr_tile % this._tiles_x;
            const tile_y = Math.floor(curr_tile / this._tiles_x);
            const tx = x + tile_x * 16;
            const ty = y + tile_y * 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
                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;
                }

                display.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;

                        display.subTile(sx, sy, sw, sh, color);
                    }
                }
                display.finishTile();
            }
            sock.set_rQi(rQi);
            this._lastsubencoding = subencoding;
            this._tiles--;
        }

        return true;
    }
}