]>
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
], rQ
[rQi
+ 1], rQ
[rQi
+ 2]], 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
=== 0) {
155 if (uncompressedSize
< 12) {
156 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
160 data
= sock
.rQshiftBytes(uncompressedSize
);
162 data
= this._readData(sock
);
167 this._zlibs
[streamId
].setInput(data
);
168 data
= this._zlibs
[streamId
].inflate(uncompressedSize
);
169 this._zlibs
[streamId
].setInput(null);
172 let rgbx
= new Uint8Array(width
* height
* 4);
173 for (let i
= 0, j
= 0; i
< width
* height
* 4; i
+= 4, j
+= 3) {
175 rgbx
[i
+ 1] = data
[j
+ 1];
176 rgbx
[i
+ 2] = data
[j
+ 2];
177 rgbx
[i
+ 3] = 255; // Alpha
180 display
.blitImage(x
, y
, width
, height
, rgbx
, 0, false);
185 _paletteFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
186 if (this._numColors
=== 0) {
187 if (sock
.rQwait("TIGHT palette", 1)) {
191 const numColors
= sock
.rQpeek8() + 1;
192 const paletteSize
= numColors
* 3;
194 if (sock
.rQwait("TIGHT palette", 1 + paletteSize
)) {
198 this._numColors
= numColors
;
201 sock
.rQshiftTo(this._palette
, paletteSize
);
204 const bpp
= (this._numColors
<= 2) ? 1 : 8;
205 const rowSize
= Math
.floor((width
* bpp
+ 7) / 8);
206 const uncompressedSize
= rowSize
* height
;
210 if (uncompressedSize
=== 0) {
214 if (uncompressedSize
< 12) {
215 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
219 data
= sock
.rQshiftBytes(uncompressedSize
);
221 data
= this._readData(sock
);
226 this._zlibs
[streamId
].setInput(data
);
227 data
= this._zlibs
[streamId
].inflate(uncompressedSize
);
228 this._zlibs
[streamId
].setInput(null);
231 // Convert indexed (palette based) image data to RGB
232 if (this._numColors
== 2) {
233 this._monoRect(x
, y
, width
, height
, data
, this._palette
, display
);
235 this._paletteRect(x
, y
, width
, height
, data
, this._palette
, display
);
243 _monoRect(x
, y
, width
, height
, data
, palette
, display
) {
244 // Convert indexed (palette based) image data to RGB
245 // TODO: reduce number of calculations inside loop
246 const dest
= this._getScratchBuffer(width
* height
* 4);
247 const w
= Math
.floor((width
+ 7) / 8);
248 const w1
= Math
.floor(width
/ 8);
250 for (let y
= 0; y
< height
; y
++) {
252 for (x
= 0; x
< w1
; x
++) {
253 for (let b
= 7; b
>= 0; b
--) {
254 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
255 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
256 dest
[dp
] = palette
[sp
];
257 dest
[dp
+ 1] = palette
[sp
+ 1];
258 dest
[dp
+ 2] = palette
[sp
+ 2];
263 for (let b
= 7; b
>= 8 - width
% 8; b
--) {
264 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
265 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
266 dest
[dp
] = palette
[sp
];
267 dest
[dp
+ 1] = palette
[sp
+ 1];
268 dest
[dp
+ 2] = palette
[sp
+ 2];
273 display
.blitImage(x
, y
, width
, height
, dest
, 0, false);
276 _paletteRect(x
, y
, width
, height
, data
, palette
, display
) {
277 // Convert indexed (palette based) image data to RGB
278 const dest
= this._getScratchBuffer(width
* height
* 4);
279 const total
= width
* height
* 4;
280 for (let i
= 0, j
= 0; i
< total
; i
+= 4, j
++) {
281 const sp
= data
[j
] * 3;
282 dest
[i
] = palette
[sp
];
283 dest
[i
+ 1] = palette
[sp
+ 1];
284 dest
[i
+ 2] = palette
[sp
+ 2];
288 display
.blitImage(x
, y
, width
, height
, dest
, 0, false);
291 _gradientFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
292 throw new Error("Gradient filter not implemented");
296 if (this._len
=== 0) {
297 if (sock
.rQwait("TIGHT", 3)) {
303 byte = sock
.rQshift8();
304 this._len
= byte & 0x7f;
306 byte = sock
.rQshift8();
307 this._len
|= (byte & 0x7f) << 7;
309 byte = sock
.rQshift8();
310 this._len
|= byte << 14;
315 if (sock
.rQwait("TIGHT", this._len
)) {
319 let data
= sock
.rQshiftBytes(this._len
);
325 _getScratchBuffer(size
) {
326 if (!this._scratchBuffer
|| (this._scratchBuffer
.length
< size
)) {
327 this._scratchBuffer
= new Uint8Array(size
);
329 return this._scratchBuffer
;