hextile.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. import * as Log from '../util/logging.js';
  10. export default class HextileDecoder {
  11. constructor() {
  12. this._tiles = 0;
  13. this._lastsubencoding = 0;
  14. this._tileBuffer = new Uint8Array(16 * 16 * 4);
  15. }
  16. decodeRect(x, y, width, height, sock, display, depth) {
  17. if (this._tiles === 0) {
  18. this._tilesX = Math.ceil(width / 16);
  19. this._tilesY = Math.ceil(height / 16);
  20. this._totalTiles = this._tilesX * this._tilesY;
  21. this._tiles = this._totalTiles;
  22. }
  23. while (this._tiles > 0) {
  24. let bytes = 1;
  25. if (sock.rQwait("HEXTILE", bytes)) {
  26. return false;
  27. }
  28. let rQ = sock.rQ;
  29. let rQi = sock.rQi;
  30. let subencoding = rQ[rQi]; // Peek
  31. if (subencoding > 30) { // Raw
  32. throw new Error("Illegal hextile subencoding (subencoding: " +
  33. subencoding + ")");
  34. }
  35. const currTile = this._totalTiles - this._tiles;
  36. const tileX = currTile % this._tilesX;
  37. const tileY = Math.floor(currTile / this._tilesX);
  38. const tx = x + tileX * 16;
  39. const ty = y + tileY * 16;
  40. const tw = Math.min(16, (x + width) - tx);
  41. const th = Math.min(16, (y + height) - ty);
  42. // Figure out how much we are expecting
  43. if (subencoding & 0x01) { // Raw
  44. bytes += tw * th * 4;
  45. } else {
  46. if (subencoding & 0x02) { // Background
  47. bytes += 4;
  48. }
  49. if (subencoding & 0x04) { // Foreground
  50. bytes += 4;
  51. }
  52. if (subencoding & 0x08) { // AnySubrects
  53. bytes++; // Since we aren't shifting it off
  54. if (sock.rQwait("HEXTILE", bytes)) {
  55. return false;
  56. }
  57. let subrects = rQ[rQi + bytes - 1]; // Peek
  58. if (subencoding & 0x10) { // SubrectsColoured
  59. bytes += subrects * (4 + 2);
  60. } else {
  61. bytes += subrects * 2;
  62. }
  63. }
  64. }
  65. if (sock.rQwait("HEXTILE", bytes)) {
  66. return false;
  67. }
  68. // We know the encoding and have a whole tile
  69. rQi++;
  70. if (subencoding === 0) {
  71. if (this._lastsubencoding & 0x01) {
  72. // Weird: ignore blanks are RAW
  73. Log.Debug(" Ignoring blank after RAW");
  74. } else {
  75. display.fillRect(tx, ty, tw, th, this._background);
  76. }
  77. } else if (subencoding & 0x01) { // Raw
  78. let pixels = tw * th;
  79. // Max sure the image is fully opaque
  80. for (let i = 0;i < pixels;i++) {
  81. rQ[rQi + i * 4 + 3] = 255;
  82. }
  83. display.blitImage(tx, ty, tw, th, rQ, rQi);
  84. rQi += bytes - 1;
  85. } else {
  86. if (subencoding & 0x02) { // Background
  87. this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
  88. rQi += 4;
  89. }
  90. if (subencoding & 0x04) { // Foreground
  91. this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
  92. rQi += 4;
  93. }
  94. this._startTile(tx, ty, tw, th, this._background);
  95. if (subencoding & 0x08) { // AnySubrects
  96. let subrects = rQ[rQi];
  97. rQi++;
  98. for (let s = 0; s < subrects; s++) {
  99. let color;
  100. if (subencoding & 0x10) { // SubrectsColoured
  101. color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
  102. rQi += 4;
  103. } else {
  104. color = this._foreground;
  105. }
  106. const xy = rQ[rQi];
  107. rQi++;
  108. const sx = (xy >> 4);
  109. const sy = (xy & 0x0f);
  110. const wh = rQ[rQi];
  111. rQi++;
  112. const sw = (wh >> 4) + 1;
  113. const sh = (wh & 0x0f) + 1;
  114. this._subTile(sx, sy, sw, sh, color);
  115. }
  116. }
  117. this._finishTile(display);
  118. }
  119. sock.rQi = rQi;
  120. this._lastsubencoding = subencoding;
  121. this._tiles--;
  122. }
  123. return true;
  124. }
  125. // start updating a tile
  126. _startTile(x, y, width, height, color) {
  127. this._tileX = x;
  128. this._tileY = y;
  129. this._tileW = width;
  130. this._tileH = height;
  131. const red = color[0];
  132. const green = color[1];
  133. const blue = color[2];
  134. const data = this._tileBuffer;
  135. for (let i = 0; i < width * height * 4; i += 4) {
  136. data[i] = red;
  137. data[i + 1] = green;
  138. data[i + 2] = blue;
  139. data[i + 3] = 255;
  140. }
  141. }
  142. // update sub-rectangle of the current tile
  143. _subTile(x, y, w, h, color) {
  144. const red = color[0];
  145. const green = color[1];
  146. const blue = color[2];
  147. const xend = x + w;
  148. const yend = y + h;
  149. const data = this._tileBuffer;
  150. const width = this._tileW;
  151. for (let j = y; j < yend; j++) {
  152. for (let i = x; i < xend; i++) {
  153. const p = (i + (j * width)) * 4;
  154. data[p] = red;
  155. data[p + 1] = green;
  156. data[p + 2] = blue;
  157. data[p + 3] = 255;
  158. }
  159. }
  160. }
  161. // draw the current tile to the screen
  162. _finishTile(display) {
  163. display.blitImage(this._tileX, this._tileY,
  164. this._tileW, this._tileH,
  165. this._tileBuffer, 0);
  166. }
  167. }