jpeg.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2019 The noVNC Authors
  4. * Licensed under MPL 2.0 (see LICENSE.txt)
  5. *
  6. * See README.md for usage and integration instructions.
  7. *
  8. */
  9. export default class JPEGDecoder {
  10. constructor() {
  11. // RealVNC will reuse the quantization tables
  12. // and Huffman tables, so we need to cache them.
  13. this._cachedQuantTables = [];
  14. this._cachedHuffmanTables = [];
  15. this._segments = [];
  16. }
  17. decodeRect(x, y, width, height, sock, display, depth) {
  18. // A rect of JPEG encodings is simply a JPEG file
  19. while (true) {
  20. let segment = this._readSegment(sock);
  21. if (segment === null) {
  22. return false;
  23. }
  24. this._segments.push(segment);
  25. // End of image?
  26. if (segment[1] === 0xD9) {
  27. break;
  28. }
  29. }
  30. let huffmanTables = [];
  31. let quantTables = [];
  32. for (let segment of this._segments) {
  33. let type = segment[1];
  34. if (type === 0xC4) {
  35. // Huffman tables
  36. huffmanTables.push(segment);
  37. } else if (type === 0xDB) {
  38. // Quantization tables
  39. quantTables.push(segment);
  40. }
  41. }
  42. const sofIndex = this._segments.findIndex(
  43. x => x[1] == 0xC0 || x[1] == 0xC2
  44. );
  45. if (sofIndex == -1) {
  46. throw new Error("Illegal JPEG image without SOF");
  47. }
  48. if (quantTables.length === 0) {
  49. this._segments.splice(sofIndex+1, 0,
  50. ...this._cachedQuantTables);
  51. }
  52. if (huffmanTables.length === 0) {
  53. this._segments.splice(sofIndex+1, 0,
  54. ...this._cachedHuffmanTables);
  55. }
  56. let length = 0;
  57. for (let segment of this._segments) {
  58. length += segment.length;
  59. }
  60. let data = new Uint8Array(length);
  61. length = 0;
  62. for (let segment of this._segments) {
  63. data.set(segment, length);
  64. length += segment.length;
  65. }
  66. display.imageRect(x, y, width, height, "image/jpeg", data);
  67. if (huffmanTables.length !== 0) {
  68. this._cachedHuffmanTables = huffmanTables;
  69. }
  70. if (quantTables.length !== 0) {
  71. this._cachedQuantTables = quantTables;
  72. }
  73. this._segments = [];
  74. return true;
  75. }
  76. _readSegment(sock) {
  77. if (sock.rQwait("JPEG", 2)) {
  78. return null;
  79. }
  80. let marker = sock.rQshift8();
  81. if (marker != 0xFF) {
  82. throw new Error("Illegal JPEG marker received (byte: " +
  83. marker + ")");
  84. }
  85. let type = sock.rQshift8();
  86. if (type >= 0xD0 && type <= 0xD9 || type == 0x01) {
  87. // No length after marker
  88. return new Uint8Array([marker, type]);
  89. }
  90. if (sock.rQwait("JPEG", 2, 2)) {
  91. return null;
  92. }
  93. let length = sock.rQshift16();
  94. if (length < 2) {
  95. throw new Error("Illegal JPEG length received (length: " +
  96. length + ")");
  97. }
  98. if (sock.rQwait("JPEG", length-2, 4)) {
  99. return null;
  100. }
  101. let extra = 0;
  102. if (type === 0xDA) {
  103. // start of scan
  104. extra += 2;
  105. while (true) {
  106. if (sock.rQwait("JPEG", length-2+extra, 4)) {
  107. return null;
  108. }
  109. let data = sock.rQpeekBytes(length-2+extra, false);
  110. if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
  111. !(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
  112. extra -= 2;
  113. break;
  114. }
  115. extra++;
  116. }
  117. }
  118. let segment = new Uint8Array(2 + length + extra);
  119. segment[0] = marker;
  120. segment[1] = type;
  121. segment[2] = length >> 8;
  122. segment[3] = length;
  123. segment.set(sock.rQshiftBytes(length-2+extra, false), 4);
  124. return segment;
  125. }
  126. }