mirror of
				https://git.proxmox.com/git/mirror_novnc
				synced 2025-10-31 09:01:41 +00:00 
			
		
		
		
	 87143b361e
			
		
	
	
		87143b361e
		
	
	
	
	
		
			
			We don't have to keep track of this much data between rects, so restructure things to make it more simple. This allows the JPEG parsing code to be a pure function which only depends on the input.
		
			
				
	
	
		
			147 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
|  * noVNC: HTML5 VNC client
 | |
|  * Copyright (C) 2019 The noVNC Authors
 | |
|  * Licensed under MPL 2.0 (see LICENSE.txt)
 | |
|  *
 | |
|  * See README.md for usage and integration instructions.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| export default class JPEGDecoder {
 | |
|     constructor() {
 | |
|         // RealVNC will reuse the quantization tables
 | |
|         // and Huffman tables, so we need to cache them.
 | |
|         this._cachedQuantTables = [];
 | |
|         this._cachedHuffmanTables = [];
 | |
| 
 | |
|         this._segments = [];
 | |
|     }
 | |
| 
 | |
|     decodeRect(x, y, width, height, sock, display, depth) {
 | |
|         // A rect of JPEG encodings is simply a JPEG file
 | |
|         while (true) {
 | |
|             let segment = this._readSegment(sock);
 | |
|             if (segment === null) {
 | |
|                 return false;
 | |
|             }
 | |
|             this._segments.push(segment);
 | |
|             // End of image?
 | |
|             if (segment[1] === 0xD9) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         let huffmanTables = [];
 | |
|         let quantTables = [];
 | |
|         for (let segment of this._segments) {
 | |
|             let type = segment[1];
 | |
|             if (type === 0xC4) {
 | |
|                 // Huffman tables
 | |
|                 huffmanTables.push(segment);
 | |
|             } else if (type === 0xDB) {
 | |
|                 // Quantization tables
 | |
|                 quantTables.push(segment);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         const sofIndex = this._segments.findIndex(
 | |
|             x => x[1] == 0xC0 || x[1] == 0xC2
 | |
|         );
 | |
|         if (sofIndex == -1) {
 | |
|             throw new Error("Illegal JPEG image without SOF");
 | |
|         }
 | |
| 
 | |
|         if (quantTables.length === 0) {
 | |
|             this._segments.splice(sofIndex+1, 0,
 | |
|                                   ...this._cachedQuantTables);
 | |
|         }
 | |
|         if (huffmanTables.length === 0) {
 | |
|             this._segments.splice(sofIndex+1, 0,
 | |
|                                   ...this._cachedHuffmanTables);
 | |
|         }
 | |
| 
 | |
|         let length = 0;
 | |
|         for (let segment of this._segments) {
 | |
|             length += segment.length;
 | |
|         }
 | |
| 
 | |
|         let data = new Uint8Array(length);
 | |
|         length = 0;
 | |
|         for (let segment of this._segments) {
 | |
|             data.set(segment, length);
 | |
|             length += segment.length;
 | |
|         }
 | |
| 
 | |
|         display.imageRect(x, y, width, height, "image/jpeg", data);
 | |
| 
 | |
|         if (huffmanTables.length !== 0) {
 | |
|             this._cachedHuffmanTables = huffmanTables;
 | |
|         }
 | |
|         if (quantTables.length !== 0) {
 | |
|             this._cachedQuantTables = quantTables;
 | |
|         }
 | |
| 
 | |
|         this._segments = [];
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     _readSegment(sock) {
 | |
|         if (sock.rQwait("JPEG", 2)) {
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         let marker = sock.rQshift8();
 | |
|         if (marker != 0xFF) {
 | |
|             throw new Error("Illegal JPEG marker received (byte: " +
 | |
|                                marker + ")");
 | |
|         }
 | |
|         let type = sock.rQshift8();
 | |
|         if (type >= 0xD0 && type <= 0xD9 || type == 0x01) {
 | |
|             // No length after marker
 | |
|             return new Uint8Array([marker, type]);
 | |
|         }
 | |
| 
 | |
|         if (sock.rQwait("JPEG", 2, 2)) {
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         let length = sock.rQshift16();
 | |
|         if (length < 2) {
 | |
|             throw new Error("Illegal JPEG length received (length: " +
 | |
|                                length + ")");
 | |
|         }
 | |
| 
 | |
|         if (sock.rQwait("JPEG", length-2, 4)) {
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         let extra = 0;
 | |
|         if (type === 0xDA) {
 | |
|             // start of scan
 | |
|             extra += 2;
 | |
|             while (true) {
 | |
|                 if (sock.rQwait("JPEG", length-2+extra, 4)) {
 | |
|                     return null;
 | |
|                 }
 | |
|                 let data = sock.rQslice(0, length-2+extra);
 | |
|                 if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
 | |
|                     !(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
 | |
|                     extra -= 2;
 | |
|                     break;
 | |
|                 }
 | |
|                 extra++;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         let segment = new Uint8Array(2 + length + extra);
 | |
|         segment[0] = marker;
 | |
|         segment[1] = type;
 | |
|         segment[2] = length >> 8;
 | |
|         segment[3] = length;
 | |
|         segment.set(sock.rQshiftBytes(length-2+extra), 4);
 | |
| 
 | |
|         return segment;
 | |
|     }
 | |
| }
 |