You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

HextileEncoder.cxx 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2005 Constantin Kaplinsky. All Rights Reserved.
  3. * Copyright 2014-2022 Pierre Ossman for Cendio AB
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. #ifdef HAVE_CONFIG_H
  21. #include <config.h>
  22. #endif
  23. #include <rfb/encodings.h>
  24. #include <rfb/SConnection.h>
  25. #include <rfb/HextileEncoder.h>
  26. #include <rfb/Palette.h>
  27. #include <rfb/PixelBuffer.h>
  28. #include <rfb/Configuration.h>
  29. #include <rfb/hextileConstants.h>
  30. using namespace rfb;
  31. BoolParameter improvedHextile("ImprovedHextile",
  32. "Use improved compression algorithm for Hextile "
  33. "encoding which achieves better compression "
  34. "ratios by the cost of using more CPU time",
  35. true);
  36. HextileEncoder::HextileEncoder(SConnection* conn) :
  37. Encoder(conn, encodingHextile, EncoderPlain)
  38. {
  39. }
  40. HextileEncoder::~HextileEncoder()
  41. {
  42. }
  43. bool HextileEncoder::isSupported()
  44. {
  45. return conn->client.supportsEncoding(encodingHextile);
  46. }
  47. void HextileEncoder::writeRect(const PixelBuffer* pb,
  48. const Palette& /*palette*/)
  49. {
  50. rdr::OutStream* os = conn->getOutStream();
  51. switch (pb->getPF().bpp) {
  52. case 8:
  53. if (improvedHextile) {
  54. hextileEncodeBetter<uint8_t>(os, pb);
  55. } else {
  56. hextileEncode<uint8_t>(os, pb);
  57. }
  58. break;
  59. case 16:
  60. if (improvedHextile) {
  61. hextileEncodeBetter<uint16_t>(os, pb);
  62. } else {
  63. hextileEncode<uint16_t>(os, pb);
  64. }
  65. break;
  66. case 32:
  67. if (improvedHextile) {
  68. hextileEncodeBetter<uint32_t>(os, pb);
  69. } else {
  70. hextileEncode<uint32_t>(os, pb);
  71. }
  72. break;
  73. }
  74. }
  75. void HextileEncoder::writeSolidRect(int width, int height,
  76. const PixelFormat& pf,
  77. const uint8_t* colour)
  78. {
  79. rdr::OutStream* os;
  80. int tiles;
  81. os = conn->getOutStream();
  82. tiles = ((width + 15)/16) * ((height + 15)/16);
  83. os->writeU8(hextileBgSpecified);
  84. os->writeBytes(colour, pf.bpp/8);
  85. tiles--;
  86. while (tiles--)
  87. os->writeU8(0);
  88. }
  89. template<class T>
  90. inline void HextileEncoder::writePixel(rdr::OutStream* os, T pixel)
  91. {
  92. if (sizeof(T) == 1)
  93. os->writeOpaque8(pixel);
  94. else if (sizeof(T) == 2)
  95. os->writeOpaque16(pixel);
  96. else if (sizeof(T) == 4)
  97. os->writeOpaque32(pixel);
  98. }
  99. template<class T>
  100. void HextileEncoder::hextileEncode(rdr::OutStream* os,
  101. const PixelBuffer* pb)
  102. {
  103. Rect t;
  104. T buf[256];
  105. T oldBg = 0, oldFg = 0;
  106. bool oldBgValid = false;
  107. bool oldFgValid = false;
  108. uint8_t encoded[256*sizeof(T)];
  109. for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) {
  110. t.br.y = __rfbmin(pb->height(), t.tl.y + 16);
  111. for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) {
  112. t.br.x = __rfbmin(pb->width(), t.tl.x + 16);
  113. pb->getImage(buf, t);
  114. T bg = 0, fg = 0;
  115. int tileType = testTileType(buf, t.width(), t.height(), &bg, &fg);
  116. if (!oldBgValid || oldBg != bg) {
  117. tileType |= hextileBgSpecified;
  118. oldBg = bg;
  119. oldBgValid = true;
  120. }
  121. int encodedLen = 0;
  122. if (tileType & hextileAnySubrects) {
  123. if (tileType & hextileSubrectsColoured) {
  124. oldFgValid = false;
  125. } else {
  126. if (!oldFgValid || oldFg != fg) {
  127. tileType |= hextileFgSpecified;
  128. oldFg = fg;
  129. oldFgValid = true;
  130. }
  131. }
  132. encodedLen = hextileEncodeTile(buf, t.width(), t.height(),
  133. tileType, encoded, bg);
  134. if (encodedLen < 0) {
  135. pb->getImage(buf, t);
  136. os->writeU8(hextileRaw);
  137. os->writeBytes((const uint8_t*)buf,
  138. t.width() * t.height() * sizeof(T));
  139. oldBgValid = oldFgValid = false;
  140. continue;
  141. }
  142. }
  143. os->writeU8(tileType);
  144. if (tileType & hextileBgSpecified) writePixel(os, bg);
  145. if (tileType & hextileFgSpecified) writePixel(os, fg);
  146. if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
  147. }
  148. }
  149. }
  150. template<class T>
  151. int HextileEncoder::hextileEncodeTile(T* data, int w, int h,
  152. int tileType, uint8_t* encoded,
  153. T bg)
  154. {
  155. uint8_t* nSubrectsPtr = encoded;
  156. *nSubrectsPtr = 0;
  157. encoded++;
  158. for (int y = 0; y < h; y++)
  159. {
  160. int x = 0;
  161. while (x < w) {
  162. if (*data == bg) {
  163. x++;
  164. data++;
  165. continue;
  166. }
  167. // Find horizontal subrect first
  168. T* ptr = data+1;
  169. T* eol = data+w-x;
  170. while (ptr < eol && *ptr == *data) ptr++;
  171. int sw = ptr - data;
  172. ptr = data + w;
  173. int sh = 1;
  174. while (sh < h-y) {
  175. eol = ptr + sw;
  176. while (ptr < eol)
  177. if (*ptr++ != *data) goto endOfSubrect;
  178. ptr += w - sw;
  179. sh++;
  180. }
  181. endOfSubrect:
  182. (*nSubrectsPtr)++;
  183. if (tileType & hextileSubrectsColoured) {
  184. if (encoded - nSubrectsPtr + sizeof(T) > w*h*sizeof(T))
  185. return -1;
  186. if (sizeof(T) == 1) {
  187. *encoded++ = *data;
  188. } else if (sizeof(T) == 2) {
  189. *encoded++ = ((uint8_t*)data)[0];
  190. *encoded++ = ((uint8_t*)data)[1];
  191. } else if (sizeof(T) == 4) {
  192. *encoded++ = ((uint8_t*)data)[0];
  193. *encoded++ = ((uint8_t*)data)[1];
  194. *encoded++ = ((uint8_t*)data)[2];
  195. *encoded++ = ((uint8_t*)data)[3];
  196. }
  197. }
  198. if ((size_t)(encoded - nSubrectsPtr + 2) > w*h*sizeof(T))
  199. return -1;
  200. *encoded++ = (x << 4) | y;
  201. *encoded++ = ((sw-1) << 4) | (sh-1);
  202. ptr = data+w;
  203. T* eor = data+w*sh;
  204. while (ptr < eor) {
  205. eol = ptr + sw;
  206. while (ptr < eol) *ptr++ = bg;
  207. ptr += w - sw;
  208. }
  209. x += sw;
  210. data += sw;
  211. }
  212. }
  213. return encoded - nSubrectsPtr;
  214. }
  215. template<class T>
  216. int HextileEncoder::testTileType(T* data, int w, int h, T* bg, T* fg)
  217. {
  218. T pix1 = *data;
  219. T* end = data + w * h;
  220. T* ptr = data + 1;
  221. while (ptr < end && *ptr == pix1)
  222. ptr++;
  223. if (ptr == end) {
  224. *bg = pix1;
  225. return 0; // solid-color tile
  226. }
  227. int count1 = ptr - data;
  228. int count2 = 1;
  229. T pix2 = *ptr++;
  230. int tileType = hextileAnySubrects;
  231. for (; ptr < end; ptr++) {
  232. if (*ptr == pix1) {
  233. count1++;
  234. } else if (*ptr == pix2) {
  235. count2++;
  236. } else {
  237. tileType |= hextileSubrectsColoured;
  238. break;
  239. }
  240. }
  241. if (count1 >= count2) {
  242. *bg = pix1; *fg = pix2;
  243. } else {
  244. *bg = pix2; *fg = pix1;
  245. }
  246. return tileType;
  247. }
  248. //
  249. // This class analyzes a separate tile and encodes its subrectangles.
  250. //
  251. template<class T>
  252. class HextileTile {
  253. public:
  254. HextileTile ();
  255. //
  256. // Initialize existing object instance with new tile data.
  257. //
  258. void newTile(const T *src, int w, int h);
  259. //
  260. // Flags can include: hextileRaw, hextileAnySubrects and
  261. // hextileSubrectsColoured. Note that if hextileRaw is set, other
  262. // flags make no sense. Also, hextileSubrectsColoured is meaningful
  263. // only when hextileAnySubrects is set as well.
  264. //
  265. int getFlags() const { return m_flags; }
  266. //
  267. // Returns the size of encoded subrects data, including subrect count.
  268. // The size is zero if flags do not include hextileAnySubrects.
  269. //
  270. size_t getSize() const { return m_size; }
  271. //
  272. // Return optimal background.
  273. //
  274. int getBackground() const { return m_background; }
  275. //
  276. // Return foreground if flags include hextileSubrectsColoured.
  277. //
  278. int getForeground() const { return m_foreground; }
  279. //
  280. // Encode subrects. This function may be called only if
  281. // hextileAnySubrects bit is set in flags. The buffer size should be
  282. // big enough to store at least the number of bytes returned by the
  283. // getSize() method.
  284. //
  285. void encode(uint8_t* dst) const;
  286. protected:
  287. //
  288. // Analyze the tile pixels, fill in all the data fields.
  289. //
  290. void analyze();
  291. const T *m_tile;
  292. int m_width;
  293. int m_height;
  294. size_t m_size;
  295. int m_flags;
  296. T m_background;
  297. T m_foreground;
  298. int m_numSubrects;
  299. uint8_t m_coords[256 * 2];
  300. T m_colors[256];
  301. private:
  302. bool m_processed[16][16];
  303. Palette m_pal;
  304. };
  305. template<class T>
  306. HextileTile<T>::HextileTile()
  307. : m_tile(NULL), m_width(0), m_height(0),
  308. m_size(0), m_flags(0), m_background(0), m_foreground(0),
  309. m_numSubrects(0)
  310. {
  311. }
  312. template<class T>
  313. void HextileTile<T>::newTile(const T *src, int w, int h)
  314. {
  315. m_tile = src;
  316. m_width = w;
  317. m_height = h;
  318. analyze();
  319. }
  320. template<class T>
  321. void HextileTile<T>::analyze()
  322. {
  323. assert(m_tile && m_width && m_height);
  324. const T *ptr = m_tile;
  325. const T *end = &m_tile[m_width * m_height];
  326. T color = *ptr++;
  327. while (ptr != end && *ptr == color)
  328. ptr++;
  329. // Handle solid tile
  330. if (ptr == end) {
  331. m_background = m_tile[0];
  332. m_flags = 0;
  333. m_size = 0;
  334. return;
  335. }
  336. // Compute number of complete rows of the same color, at the top
  337. int y = (ptr - m_tile) / m_width;
  338. T *colorsPtr = m_colors;
  339. uint8_t *coordsPtr = m_coords;
  340. m_pal.clear();
  341. m_numSubrects = 0;
  342. // Have we found the first subrect already?
  343. if (y > 0) {
  344. *colorsPtr++ = color;
  345. *coordsPtr++ = 0;
  346. *coordsPtr++ = (uint8_t)(((m_width - 1) << 4) | ((y - 1) & 0x0F));
  347. m_pal.insert(color, 1);
  348. m_numSubrects++;
  349. }
  350. memset(m_processed, 0, 16 * 16 * sizeof(bool));
  351. int x, sx, sy, sw, sh, max_x;
  352. for (; y < m_height; y++) {
  353. for (x = 0; x < m_width; x++) {
  354. // Skip pixels that were processed earlier
  355. if (m_processed[y][x]) {
  356. continue;
  357. }
  358. // Determine dimensions of the horizontal subrect
  359. color = m_tile[y * m_width + x];
  360. for (sx = x + 1; sx < m_width; sx++) {
  361. if (m_tile[y * m_width + sx] != color)
  362. break;
  363. }
  364. sw = sx - x;
  365. max_x = sx;
  366. for (sy = y + 1; sy < m_height; sy++) {
  367. for (sx = x; sx < max_x; sx++) {
  368. if (m_tile[sy * m_width + sx] != color)
  369. goto done;
  370. }
  371. }
  372. done:
  373. sh = sy - y;
  374. // Save properties of this subrect
  375. *colorsPtr++ = color;
  376. *coordsPtr++ = (uint8_t)((x << 4) | (y & 0x0F));
  377. *coordsPtr++ = (uint8_t)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
  378. if (!m_pal.insert(color, 1) ||
  379. ((size_t)m_pal.size() > (48 + 2 * sizeof(T)*8))) {
  380. // Handle palette overflow
  381. m_flags = hextileRaw;
  382. m_size = 0;
  383. return;
  384. }
  385. m_numSubrects++;
  386. // Mark pixels of this subrect as processed, below this row
  387. for (sy = y + 1; sy < y + sh; sy++) {
  388. for (sx = x; sx < x + sw; sx++)
  389. m_processed[sy][sx] = true;
  390. }
  391. // Skip processed pixels of this row
  392. x += (sw - 1);
  393. }
  394. }
  395. // Save number of colors in this tile (should be no less than 2)
  396. int numColors = m_pal.size();
  397. assert(numColors >= 2);
  398. m_background = (T)m_pal.getColour(0);
  399. m_flags = hextileAnySubrects;
  400. int numSubrects = m_numSubrects - m_pal.getCount(0);
  401. if (numColors == 2) {
  402. // Monochrome tile
  403. m_foreground = (T)m_pal.getColour(1);
  404. m_size = 1 + 2 * numSubrects;
  405. } else {
  406. // Colored tile
  407. m_flags |= hextileSubrectsColoured;
  408. m_size = 1 + (2 + sizeof(T)) * numSubrects;
  409. }
  410. }
  411. template<class T>
  412. void HextileTile<T>::encode(uint8_t *dst) const
  413. {
  414. assert(m_numSubrects && (m_flags & hextileAnySubrects));
  415. // Zero subrects counter
  416. uint8_t *numSubrectsPtr = dst;
  417. *dst++ = 0;
  418. for (int i = 0; i < m_numSubrects; i++) {
  419. if (m_colors[i] == m_background)
  420. continue;
  421. if (m_flags & hextileSubrectsColoured) {
  422. if (sizeof(T) == 1) {
  423. *dst++ = m_colors[i];
  424. } else if (sizeof(T) == 2) {
  425. *dst++ = ((uint8_t*)&m_colors[i])[0];
  426. *dst++ = ((uint8_t*)&m_colors[i])[1];
  427. } else if (sizeof(T) == 4) {
  428. *dst++ = ((uint8_t*)&m_colors[i])[0];
  429. *dst++ = ((uint8_t*)&m_colors[i])[1];
  430. *dst++ = ((uint8_t*)&m_colors[i])[2];
  431. *dst++ = ((uint8_t*)&m_colors[i])[3];
  432. }
  433. }
  434. *dst++ = m_coords[i * 2];
  435. *dst++ = m_coords[i * 2 + 1];
  436. (*numSubrectsPtr)++;
  437. }
  438. assert((size_t)(dst - numSubrectsPtr) == m_size);
  439. }
  440. //
  441. // Main encoding function.
  442. //
  443. template<class T>
  444. void HextileEncoder::hextileEncodeBetter(rdr::OutStream* os,
  445. const PixelBuffer* pb)
  446. {
  447. Rect t;
  448. T buf[256];
  449. T oldBg = 0, oldFg = 0;
  450. bool oldBgValid = false;
  451. bool oldFgValid = false;
  452. uint8_t encoded[256*sizeof(T)];
  453. HextileTile<T> tile;
  454. for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) {
  455. t.br.y = __rfbmin(pb->height(), t.tl.y + 16);
  456. for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) {
  457. t.br.x = __rfbmin(pb->width(), t.tl.x + 16);
  458. pb->getImage(buf, t);
  459. tile.newTile(buf, t.width(), t.height());
  460. int tileType = tile.getFlags();
  461. size_t encodedLen = tile.getSize();
  462. if ( (tileType & hextileRaw) != 0 ||
  463. encodedLen >= t.width() * t.height() * sizeof(T)) {
  464. os->writeU8(hextileRaw);
  465. os->writeBytes((const uint8_t*)buf,
  466. t.width() * t.height() * sizeof(T));
  467. oldBgValid = oldFgValid = false;
  468. continue;
  469. }
  470. T bg = tile.getBackground();
  471. T fg = 0;
  472. if (!oldBgValid || oldBg != bg) {
  473. tileType |= hextileBgSpecified;
  474. oldBg = bg;
  475. oldBgValid = true;
  476. }
  477. if (tileType & hextileAnySubrects) {
  478. if (tileType & hextileSubrectsColoured) {
  479. oldFgValid = false;
  480. } else {
  481. fg = tile.getForeground();
  482. if (!oldFgValid || oldFg != fg) {
  483. tileType |= hextileFgSpecified;
  484. oldFg = fg;
  485. oldFgValid = true;
  486. }
  487. }
  488. tile.encode(encoded);
  489. }
  490. os->writeU8(tileType);
  491. if (tileType & hextileBgSpecified) writePixel(os, bg);
  492. if (tileType & hextileFgSpecified) writePixel(os, fg);
  493. if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
  494. }
  495. }
  496. }