]>
git.proxmox.com Git - mirror_novnc.git/blob - core/decoders/tight.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
5 * Copyright (C) 2018 Samuel Mannehed for Cendio AB
6 * Copyright (C) 2018 Pierre Ossman for Cendio AB
7 * Licensed under MPL 2.0 (see LICENSE.txt)
9 * See README.md for usage and integration instructions.
13 import * as Log
from '../util/logging.js';
14 import Inflator
from "../inflator.js";
16 export default class TightDecoder
{
21 this._palette
= new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
25 for (let i
= 0; i
< 4; i
++) {
26 this._zlibs
[i
] = new Inflator();
30 decodeRect(x
, y
, width
, height
, sock
, display
, depth
) {
31 if (this._ctl
=== null) {
32 if (sock
.rQwait("TIGHT compression-control", 1)) {
36 this._ctl
= sock
.rQshift8();
38 // Reset streams if the server requests it
39 for (let i
= 0; i
< 4; i
++) {
40 if ((this._ctl
>> i
) & 1) {
41 this._zlibs
[i
].reset();
42 Log
.Info("Reset zlib stream " + i
);
47 this._ctl
= this._ctl
>> 4;
52 if (this._ctl
=== 0x08) {
53 ret
= this._fillRect(x
, y
, width
, height
,
54 sock
, display
, depth
);
55 } else if (this._ctl
=== 0x09) {
56 ret
= this._jpegRect(x
, y
, width
, height
,
57 sock
, display
, depth
);
58 } else if (this._ctl
=== 0x0A) {
59 ret
= this._pngRect(x
, y
, width
, height
,
60 sock
, display
, depth
);
61 } else if ((this._ctl
& 0x80) == 0) {
62 ret
= this._basicRect(this._ctl
, x
, y
, width
, height
,
63 sock
, display
, depth
);
65 throw new Error("Illegal tight compression received (ctl: " +
76 _fillRect(x
, y
, width
, height
, sock
, display
, depth
) {
77 if (sock
.rQwait("TIGHT", 3)) {
84 display
.fillRect(x
, y
, width
, height
,
85 [rQ
[rQi
+ 2], rQ
[rQi
+ 1], rQ
[rQi
]], false);
91 _jpegRect(x
, y
, width
, height
, sock
, display
, depth
) {
92 let data
= this._readData(sock
);
97 display
.imageRect(x
, y
, "image/jpeg", data
);
102 _pngRect(x
, y
, width
, height
, sock
, display
, depth
) {
103 throw new Error("PNG received in standard Tight rect");
106 _basicRect(ctl
, x
, y
, width
, height
, sock
, display
, depth
) {
107 if (this._filter
=== null) {
109 if (sock
.rQwait("TIGHT", 1)) {
113 this._filter
= sock
.rQshift8();
115 // Implicit CopyFilter
120 let streamId
= ctl
& 0x3;
124 switch (this._filter
) {
125 case 0: // CopyFilter
126 ret
= this._copyFilter(streamId
, x
, y
, width
, height
,
127 sock
, display
, depth
);
129 case 1: // PaletteFilter
130 ret
= this._paletteFilter(streamId
, x
, y
, width
, height
,
131 sock
, display
, depth
);
133 case 2: // GradientFilter
134 ret
= this._gradientFilter(streamId
, x
, y
, width
, height
,
135 sock
, display
, depth
);
138 throw new Error("Illegal tight filter received (ctl: " +
149 _copyFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
150 const uncompressedSize
= width
* height
* 3;
153 if (uncompressedSize
< 12) {
154 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
158 data
= sock
.rQshiftBytes(uncompressedSize
);
160 data
= this._readData(sock
);
165 data
= this._zlibs
[streamId
].inflate(data
, true, uncompressedSize
);
166 if (data
.length
!= uncompressedSize
) {
167 throw new Error("Incomplete zlib block");
171 display
.blitRgbImage(x
, y
, width
, height
, data
, 0, false);
176 _paletteFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
177 if (this._numColors
=== 0) {
178 if (sock
.rQwait("TIGHT palette", 1)) {
182 const numColors
= sock
.rQpeek8() + 1;
183 const paletteSize
= numColors
* 3;
185 if (sock
.rQwait("TIGHT palette", 1 + paletteSize
)) {
189 this._numColors
= numColors
;
192 sock
.rQshiftTo(this._palette
, paletteSize
);
195 const bpp
= (this._numColors
<= 2) ? 1 : 8;
196 const rowSize
= Math
.floor((width
* bpp
+ 7) / 8);
197 const uncompressedSize
= rowSize
* height
;
201 if (uncompressedSize
< 12) {
202 if (sock
.rQwait("TIGHT", uncompressedSize
)) {
206 data
= sock
.rQshiftBytes(uncompressedSize
);
208 data
= this._readData(sock
);
213 data
= this._zlibs
[streamId
].inflate(data
, true, uncompressedSize
);
214 if (data
.length
!= uncompressedSize
) {
215 throw new Error("Incomplete zlib block");
219 // Convert indexed (palette based) image data to RGB
220 if (this._numColors
== 2) {
221 this._monoRect(x
, y
, width
, height
, data
, this._palette
, display
);
223 this._paletteRect(x
, y
, width
, height
, data
, this._palette
, display
);
231 _monoRect(x
, y
, width
, height
, data
, palette
, display
) {
232 // Convert indexed (palette based) image data to RGB
233 // TODO: reduce number of calculations inside loop
234 const dest
= this._getScratchBuffer(width
* height
* 4);
235 const w
= Math
.floor((width
+ 7) / 8);
236 const w1
= Math
.floor(width
/ 8);
238 for (let y
= 0; y
< height
; y
++) {
240 for (x
= 0; x
< w1
; x
++) {
241 for (let b
= 7; b
>= 0; b
--) {
242 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
243 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
244 dest
[dp
] = palette
[sp
];
245 dest
[dp
+ 1] = palette
[sp
+ 1];
246 dest
[dp
+ 2] = palette
[sp
+ 2];
251 for (let b
= 7; b
>= 8 - width
% 8; b
--) {
252 dp
= (y
* width
+ x
* 8 + 7 - b
) * 4;
253 sp
= (data
[y
* w
+ x
] >> b
& 1) * 3;
254 dest
[dp
] = palette
[sp
];
255 dest
[dp
+ 1] = palette
[sp
+ 1];
256 dest
[dp
+ 2] = palette
[sp
+ 2];
261 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
264 _paletteRect(x
, y
, width
, height
, data
, palette
, display
) {
265 // Convert indexed (palette based) image data to RGB
266 const dest
= this._getScratchBuffer(width
* height
* 4);
267 const total
= width
* height
* 4;
268 for (let i
= 0, j
= 0; i
< total
; i
+= 4, j
++) {
269 const sp
= data
[j
] * 3;
270 dest
[i
] = palette
[sp
];
271 dest
[i
+ 1] = palette
[sp
+ 1];
272 dest
[i
+ 2] = palette
[sp
+ 2];
276 display
.blitRgbxImage(x
, y
, width
, height
, dest
, 0, false);
279 _gradientFilter(streamId
, x
, y
, width
, height
, sock
, display
, depth
) {
280 throw new Error("Gradient filter not implemented");
284 if (this._len
=== 0) {
285 if (sock
.rQwait("TIGHT", 3)) {
291 byte = sock
.rQshift8();
292 this._len
= byte & 0x7f;
294 byte = sock
.rQshift8();
295 this._len
|= (byte & 0x7f) << 7;
297 byte = sock
.rQshift8();
298 this._len
|= byte << 14;
303 if (sock
.rQwait("TIGHT", this._len
)) {
307 let data
= sock
.rQshiftBytes(this._len
);
313 _getScratchBuffer(size
) {
314 if (!this._scratchBuffer
|| (this._scratchBuffer
.length
< size
)) {
315 this._scratchBuffer
= new Uint8Array(size
);
317 return this._scratchBuffer
;