]>
git.proxmox.com Git - mirror_novnc.git/blob - core/decoders/tight.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2019 The noVNC Authors
4 * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
5 * Licensed under MPL 2.0 (see LICENSE.txt)
7 * See README.md for usage and integration instructions.
11 import * as Log
from '../util/logging.js';
12 import Inflator
from "../inflator.js";
14 export default class TightDecoder
{
19 this._palette
= new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
23 for (let i
= 0; i
< 4; i
++) {
24 this._zlibs
[i
] = new Inflator();
28 decodeRect(x
, y
, width
, height
, sock
, display
, depth
) {
29 if (this._ctl
=== null) {
30 if (sock
.rQwait("TIGHT compression-control", 1)) {
34 this._ctl
= sock
.rQshift8();
36 // Reset streams if the server requests it
37 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
);
45 this._ctl
= this._ctl
>> 4;
50 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
);
63 throw new Error("Illegal tight compression received (ctl: " +
74 _fillRect(x
, y
, width
, height
, sock
, display
, depth
) {
75 if (sock
.rQwait("TIGHT", 3)) {
82 display
.fillRect(x
, y
, width
, height
,
83 [rQ
[rQi
+ 2], rQ
[rQi
+ 1], rQ
[rQi
]], false);
89 _jpegRect(x
, y
, width
, height
, sock
, display
, depth
) {
90 let data
= this._readData(sock
);
95 display
.imageRect(x
, y
, width
, height
, "image/jpeg", data
);
100 _pngRect(x
, y
, width
, height
, sock
, display
, depth
) {
101 throw new Error("PNG received in standard Tight rect");
104 _basicRect(ctl
, x
, y
, width
, height
, sock
, display
, depth
) {
105 if (this._filter
=== null) {
107 if (sock
.rQwait("TIGHT", 1)) {
111 this._filter
= sock
.rQshift8();
113 // Implicit CopyFilter
118 let streamId
= ctl
& 0x3;
122 switch (this._filter
) {
123 case 0: // CopyFilter
124 ret
= this._copyFilter(streamId
, x
, y
, width
, height
,
125 sock
, display
, depth
);
127 case 1: // PaletteFilter
128 ret
= this._paletteFilter(streamId
, x
, y
, width
, height
,
129 sock
, display
, depth
);
131 case 2: // GradientFilter
132 ret
= this._gradientFilter(streamId
, x
, y
, width
, height
,
133 sock
, display
, depth
);
136 throw new Error("Illegal tight filter received (ctl: " +
147 _copyFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
148 const uncompressedSize
= width
* height
* 3;
151 if (uncompressedSize
< 12) {
152 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
156 data
= sock
.rQshiftBytes(uncompressedSize
);
158 data
= this._readData(sock
);
163 this._zlibs
[streamId
].setInput(data
);
164 data
= this._zlibs
[streamId
].inflate(uncompressedSize
);
165 this._zlibs
[streamId
].setInput(null);
168 display
.blitRgbImage(x
, y
, width
, height
, data
, 0, false);
173 _paletteFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
174 if (this._numColors
=== 0) {
175 if (sock
.rQwait("TIGHT palette", 1)) {
179 const numColors
= sock
.rQpeek8() + 1;
180 const paletteSize
= numColors
* 3;
182 if (sock
.rQwait("TIGHT palette", 1 + paletteSize
)) {
186 this._numColors
= numColors
;
189 sock
.rQshiftTo(this._palette
, paletteSize
);
192 const bpp
= (this._numColors
<= 2) ? 1 : 8;
193 const rowSize
= Math
.floor((width
* bpp
+ 7) / 8);
194 const uncompressedSize
= rowSize
* height
;
198 if (uncompressedSize
< 12) {
199 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
203 data
= sock
.rQshiftBytes(uncompressedSize
);
205 data
= this._readData(sock
);
210 this._zlibs
[streamId
].setInput(data
);
211 data
= this._zlibs
[streamId
].inflate(uncompressedSize
);
212 this._zlibs
[streamId
].setInput(null);
215 // Convert indexed (palette based) image data to RGB
216 if (this._numColors
== 2) {
217 this._monoRect(x
, y
, width
, height
, data
, this._palette
, display
);
219 this._paletteRect(x
, y
, width
, height
, data
, this._palette
, display
);
227 _monoRect(x
, y
, width
, height
, data
, palette
, display
) {
228 // Convert indexed (palette based) image data to RGB
229 // TODO: reduce number of calculations inside loop
230 const dest
= this._getScratchBuffer(width
* height
* 4);
231 const w
= Math
.floor((width
+ 7) / 8);
232 const w1
= Math
.floor(width
/ 8);
234 for (let y
= 0; y
< height
; y
++) {
236 for (x
= 0; x
< w1
; x
++) {
237 for (let b
= 7; b
>= 0; b
--) {
238 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
239 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
240 dest
[dp
] = palette
[sp
];
241 dest
[dp
+ 1] = palette
[sp
+ 1];
242 dest
[dp
+ 2] = palette
[sp
+ 2];
247 for (let b
= 7; b
>= 8 - width
% 8; b
--) {
248 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
249 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
250 dest
[dp
] = palette
[sp
];
251 dest
[dp
+ 1] = palette
[sp
+ 1];
252 dest
[dp
+ 2] = palette
[sp
+ 2];
257 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
260 _paletteRect(x
, y
, width
, height
, data
, palette
, display
) {
261 // Convert indexed (palette based) image data to RGB
262 const dest
= this._getScratchBuffer(width
* height
* 4);
263 const total
= width
* height
* 4;
264 for (let i
= 0, j
= 0; i
< total
; i
+= 4, j
++) {
265 const sp
= data
[j
] * 3;
266 dest
[i
] = palette
[sp
];
267 dest
[i
+ 1] = palette
[sp
+ 1];
268 dest
[i
+ 2] = palette
[sp
+ 2];
272 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
275 _gradientFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
276 throw new Error("Gradient filter not implemented");
280 if (this._len
=== 0) {
281 if (sock
.rQwait("TIGHT", 3)) {
287 byte = sock
.rQshift8();
288 this._len
= byte & 0x7f;
290 byte = sock
.rQshift8();
291 this._len
|= (byte & 0x7f) << 7;
293 byte = sock
.rQshift8();
294 this._len
|= byte << 14;
299 if (sock
.rQwait("TIGHT", this._len
)) {
303 let data
= sock
.rQshiftBytes(this._len
);
309 _getScratchBuffer(size
) {
310 if (!this._scratchBuffer
|| (this._scratchBuffer
.length
< size
)) {
311 this._scratchBuffer
= new Uint8Array(size
);
313 return this._scratchBuffer
;