]>
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
& 0x80) == 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 data
= this._zlibs
[streamId
].inflate(data
, true, uncompressedSize
);
164 if (data
.length
!= uncompressedSize
) {
165 throw new Error("Incomplete zlib block");
169 display
.blitRgbImage(x
, y
, width
, height
, data
, 0, false);
174 _paletteFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
175 if (this._numColors
=== 0) {
176 if (sock
.rQwait("TIGHT palette", 1)) {
180 const numColors
= sock
.rQpeek8() + 1;
181 const paletteSize
= numColors
* 3;
183 if (sock
.rQwait("TIGHT palette", 1 + paletteSize
)) {
187 this._numColors
= numColors
;
190 sock
.rQshiftTo(this._palette
, paletteSize
);
193 const bpp
= (this._numColors
<= 2) ? 1 : 8;
194 const rowSize
= Math
.floor((width
* bpp
+ 7) / 8);
195 const uncompressedSize
= rowSize
* height
;
199 if (uncompressedSize
< 12) {
200 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
204 data
= sock
.rQshiftBytes(uncompressedSize
);
206 data
= this._readData(sock
);
211 data
= this._zlibs
[streamId
].inflate(data
, true, uncompressedSize
);
212 if (data
.length
!= uncompressedSize
) {
213 throw new Error("Incomplete zlib block");
217 // Convert indexed (palette based) image data to RGB
218 if (this._numColors
== 2) {
219 this._monoRect(x
, y
, width
, height
, data
, this._palette
, display
);
221 this._paletteRect(x
, y
, width
, height
, data
, this._palette
, display
);
229 _monoRect(x
, y
, width
, height
, data
, palette
, display
) {
230 // Convert indexed (palette based) image data to RGB
231 // TODO: reduce number of calculations inside loop
232 const dest
= this._getScratchBuffer(width
* height
* 4);
233 const w
= Math
.floor((width
+ 7) / 8);
234 const w1
= Math
.floor(width
/ 8);
236 for (let y
= 0; y
< height
; y
++) {
238 for (x
= 0; x
< w1
; x
++) {
239 for (let b
= 7; b
>= 0; b
--) {
240 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
241 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
242 dest
[dp
] = palette
[sp
];
243 dest
[dp
+ 1] = palette
[sp
+ 1];
244 dest
[dp
+ 2] = palette
[sp
+ 2];
249 for (let b
= 7; b
>= 8 - width
% 8; 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];
259 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
262 _paletteRect(x
, y
, width
, height
, data
, palette
, display
) {
263 // Convert indexed (palette based) image data to RGB
264 const dest
= this._getScratchBuffer(width
* height
* 4);
265 const total
= width
* height
* 4;
266 for (let i
= 0, j
= 0; i
< total
; i
+= 4, j
++) {
267 const sp
= data
[j
] * 3;
268 dest
[i
] = palette
[sp
];
269 dest
[i
+ 1] = palette
[sp
+ 1];
270 dest
[i
+ 2] = palette
[sp
+ 2];
274 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
277 _gradientFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
278 throw new Error("Gradient filter not implemented");
282 if (this._len
=== 0) {
283 if (sock
.rQwait("TIGHT", 3)) {
289 byte = sock
.rQshift8();
290 this._len
= byte & 0x7f;
292 byte = sock
.rQshift8();
293 this._len
|= (byte & 0x7f) << 7;
295 byte = sock
.rQshift8();
296 this._len
|= byte << 14;
301 if (sock
.rQwait("TIGHT", this._len
)) {
305 let data
= sock
.rQshiftBytes(this._len
);
311 _getScratchBuffer(size
) {
312 if (!this._scratchBuffer
|| (this._scratchBuffer
.length
< size
)) {
313 this._scratchBuffer
= new Uint8Array(size
);
315 return this._scratchBuffer
;