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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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(buf, t.width() * t.height() * sizeof(T));
  138. oldBgValid = oldFgValid = false;
  139. continue;
  140. }
  141. }
  142. os->writeU8(tileType);
  143. if (tileType & hextileBgSpecified) writePixel(os, bg);
  144. if (tileType & hextileFgSpecified) writePixel(os, fg);
  145. if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
  146. }
  147. }
  148. }
  149. template<class T>
  150. int HextileEncoder::hextileEncodeTile(T* data, int w, int h,
  151. int tileType, uint8_t* encoded,
  152. T bg)
  153. {
  154. uint8_t* nSubrectsPtr = encoded;
  155. *nSubrectsPtr = 0;
  156. encoded++;
  157. for (int y = 0; y < h; y++)
  158. {
  159. int x = 0;
  160. while (x < w) {
  161. if (*data == bg) {
  162. x++;
  163. data++;
  164. continue;
  165. }
  166. // Find horizontal subrect first
  167. T* ptr = data+1;
  168. T* eol = data+w-x;
  169. while (ptr < eol && *ptr == *data) ptr++;
  170. int sw = ptr - data;
  171. ptr = data + w;
  172. int sh = 1;
  173. while (sh < h-y) {
  174. eol = ptr + sw;
  175. while (ptr < eol)
  176. if (*ptr++ != *data) goto endOfSubrect;
  177. ptr += w - sw;
  178. sh++;
  179. }
  180. endOfSubrect:
  181. (*nSubrectsPtr)++;
  182. if (tileType & hextileSubrectsColoured) {
  183. if (encoded - nSubrectsPtr + sizeof(T) > w*h*sizeof(T))
  184. return -1;
  185. if (sizeof(T) == 1) {
  186. *encoded++ = *data;
  187. } else if (sizeof(T) == 2) {
  188. *encoded++ = ((uint8_t*)data)[0];
  189. *encoded++ = ((uint8_t*)data)[1];
  190. } else if (sizeof(T) == 4) {
  191. *encoded++ = ((uint8_t*)data)[0];
  192. *encoded++ = ((uint8_t*)data)[1];
  193. *encoded++ = ((uint8_t*)data)[2];
  194. *encoded++ = ((uint8_t*)data)[3];
  195. }
  196. }
  197. if ((size_t)(encoded - nSubrectsPtr + 2) > w*h*sizeof(T))
  198. return -1;
  199. *encoded++ = (x << 4) | y;
  200. *encoded++ = ((sw-1) << 4) | (sh-1);
  201. ptr = data+w;
  202. T* eor = data+w*sh;
  203. while (ptr < eor) {
  204. eol = ptr + sw;
  205. while (ptr < eol) *ptr++ = bg;
  206. ptr += w - sw;
  207. }
  208. x += sw;
  209. data += sw;
  210. }
  211. }
  212. return encoded - nSubrectsPtr;
  213. }
  214. template<class T>
  215. int HextileEncoder::testTileType(T* data, int w, int h, T* bg, T* fg)
  216. {
  217. T pix1 = *data;
  218. T* end = data + w * h;
  219. T* ptr = data + 1;
  220. while (ptr < end && *ptr == pix1)
  221. ptr++;
  222. if (ptr == end) {
  223. *bg = pix1;
  224. return 0; // solid-color tile
  225. }
  226. int count1 = ptr - data;
  227. int count2 = 1;
  228. T pix2 = *ptr++;
  229. int tileType = hextileAnySubrects;
  230. for (; ptr < end; ptr++) {
  231. if (*ptr == pix1) {
  232. count1++;
  233. } else if (*ptr == pix2) {
  234. count2++;
  235. } else {
  236. tileType |= hextileSubrectsColoured;
  237. break;
  238. }
  239. }
  240. if (count1 >= count2) {
  241. *bg = pix1; *fg = pix2;
  242. } else {
  243. *bg = pix2; *fg = pix1;
  244. }
  245. return tileType;
  246. }
  247. //
  248. // This class analyzes a separate tile and encodes its subrectangles.
  249. //
  250. template<class T>
  251. class HextileTile {
  252. public:
  253. HextileTile ();
  254. //
  255. // Initialize existing object instance with new tile data.
  256. //
  257. void newTile(const T *src, int w, int h);
  258. //
  259. // Flags can include: hextileRaw, hextileAnySubrects and
  260. // hextileSubrectsColoured. Note that if hextileRaw is set, other
  261. // flags make no sense. Also, hextileSubrectsColoured is meaningful
  262. // only when hextileAnySubrects is set as well.
  263. //
  264. int getFlags() const { return m_flags; }
  265. //
  266. // Returns the size of encoded subrects data, including subrect count.
  267. // The size is zero if flags do not include hextileAnySubrects.
  268. //
  269. size_t getSize() const { return m_size; }
  270. //
  271. // Return optimal background.
  272. //
  273. int getBackground() const { return m_background; }
  274. //
  275. // Return foreground if flags include hextileSubrectsColoured.
  276. //
  277. int getForeground() const { return m_foreground; }
  278. //
  279. // Encode subrects. This function may be called only if
  280. // hextileAnySubrects bit is set in flags. The buffer size should be
  281. // big enough to store at least the number of bytes returned by the
  282. // getSize() method.
  283. //
  284. void encode(uint8_t* dst) const;
  285. protected:
  286. //
  287. // Analyze the tile pixels, fill in all the data fields.
  288. //
  289. void analyze();
  290. const T *m_tile;
  291. int m_width;
  292. int m_height;
  293. size_t m_size;
  294. int m_flags;
  295. T m_background;
  296. T m_foreground;
  297. int m_numSubrects;
  298. uint8_t m_coords[256 * 2];
  299. T m_colors[256];
  300. private:
  301. bool m_processed[16][16];
  302. Palette m_pal;
  303. };
  304. template<class T>
  305. HextileTile<T>::HextileTile()
  306. : m_tile(NULL), m_width(0), m_height(0),
  307. m_size(0), m_flags(0), m_background(0), m_foreground(0),
  308. m_numSubrects(0)
  309. {
  310. }
  311. template<class T>
  312. void HextileTile<T>::newTile(const T *src, int w, int h)
  313. {
  314. m_tile = src;
  315. m_width = w;
  316. m_height = h;
  317. analyze();
  318. }
  319. template<class T>
  320. void HextileTile<T>::analyze()
  321. {
  322. assert(m_tile && m_width && m_height);
  323. const T *ptr = m_tile;
  324. const T *end = &m_tile[m_width * m_height];
  325. T color = *ptr++;
  326. while (ptr != end && *ptr == color)
  327. ptr++;
  328. // Handle solid tile
  329. if (ptr == end) {
  330. m_background = m_tile[0];
  331. m_flags = 0;
  332. m_size = 0;
  333. return;
  334. }
  335. // Compute number of complete rows of the same color, at the top
  336. int y = (ptr - m_tile) / m_width;
  337. T *colorsPtr = m_colors;
  338. uint8_t *coordsPtr = m_coords;
  339. m_pal.clear();
  340. m_numSubrects = 0;
  341. // Have we found the first subrect already?
  342. if (y > 0) {
  343. *colorsPtr++ = color;
  344. *coordsPtr++ = 0;
  345. *coordsPtr++ = (uint8_t)(((m_width - 1) << 4) | ((y - 1) & 0x0F));
  346. m_pal.insert(color, 1);
  347. m_numSubrects++;
  348. }
  349. memset(m_processed, 0, 16 * 16 * sizeof(bool));
  350. int x, sx, sy, sw, sh, max_x;
  351. for (; y < m_height; y++) {
  352. for (x = 0; x < m_width; x++) {
  353. // Skip pixels that were processed earlier
  354. if (m_processed[y][x]) {
  355. continue;
  356. }
  357. // Determine dimensions of the horizontal subrect
  358. color = m_tile[y * m_width + x];
  359. for (sx = x + 1; sx < m_width; sx++) {
  360. if (m_tile[y * m_width + sx] != color)
  361. break;
  362. }
  363. sw = sx - x;
  364. max_x = sx;
  365. for (sy = y + 1; sy < m_height; sy++) {
  366. for (sx = x; sx < max_x; sx++) {
  367. if (m_tile[sy * m_width + sx] != color)
  368. goto done;
  369. }
  370. }
  371. done:
  372. sh = sy - y;
  373. // Save properties of this subrect
  374. *colorsPtr++ = color;
  375. *coordsPtr++ = (uint8_t)((x << 4) | (y & 0x0F));
  376. *coordsPtr++ = (uint8_t)(((sw - 1) << 4) | ((sh - 1) & 0x0F));
  377. if (!m_pal.insert(color, 1) ||
  378. ((size_t)m_pal.size() > (48 + 2 * sizeof(T)*8))) {
  379. // Handle palette overflow
  380. m_flags = hextileRaw;
  381. m_size = 0;
  382. return;
  383. }
  384. m_numSubrects++;
  385. // Mark pixels of this subrect as processed, below this row
  386. for (sy = y + 1; sy < y + sh; sy++) {
  387. for (sx = x; sx < x + sw; sx++)
  388. m_processed[sy][sx] = true;
  389. }
  390. // Skip processed pixels of this row
  391. x += (sw - 1);
  392. }
  393. }
  394. // Save number of colors in this tile (should be no less than 2)
  395. int numColors = m_pal.size();
  396. assert(numColors >= 2);
  397. m_background = (T)m_pal.getColour(0);
  398. m_flags = hextileAnySubrects;
  399. int numSubrects = m_numSubrects - m_pal.getCount(0);
  400. if (numColors == 2) {
  401. // Monochrome tile
  402. m_foreground = (T)m_pal.getColour(1);
  403. m_size = 1 + 2 * numSubrects;
  404. } else {
  405. // Colored tile
  406. m_flags |= hextileSubrectsColoured;
  407. m_size = 1 + (2 + sizeof(T)) * numSubrects;
  408. }
  409. }
  410. template<class T>
  411. void HextileTile<T>::encode(uint8_t *dst) const
  412. {
  413. assert(m_numSubrects && (m_flags & hextileAnySubrects));
  414. // Zero subrects counter
  415. uint8_t *numSubrectsPtr = dst;
  416. *dst++ = 0;
  417. for (int i = 0; i < m_numSubrects; i++) {
  418. if (m_colors[i] == m_background)
  419. continue;
  420. if (m_flags & hextileSubrectsColoured) {
  421. if (sizeof(T) == 1) {
  422. *dst++ = m_colors[i];
  423. } else if (sizeof(T) == 2) {
  424. *dst++ = ((uint8_t*)&m_colors[i])[0];
  425. *dst++ = ((uint8_t*)&m_colors[i])[1];
  426. } else if (sizeof(T) == 4) {
  427. *dst++ = ((uint8_t*)&m_colors[i])[0];
  428. *dst++ = ((uint8_t*)&m_colors[i])[1];
  429. *dst++ = ((uint8_t*)&m_colors[i])[2];
  430. *dst++ = ((uint8_t*)&m_colors[i])[3];
  431. }
  432. }
  433. *dst++ = m_coords[i * 2];
  434. *dst++ = m_coords[i * 2 + 1];
  435. (*numSubrectsPtr)++;
  436. }
  437. assert((size_t)(dst - numSubrectsPtr) == m_size);
  438. }
  439. //
  440. // Main encoding function.
  441. //
  442. template<class T>
  443. void HextileEncoder::hextileEncodeBetter(rdr::OutStream* os,
  444. const PixelBuffer* pb)
  445. {
  446. Rect t;
  447. T buf[256];
  448. T oldBg = 0, oldFg = 0;
  449. bool oldBgValid = false;
  450. bool oldFgValid = false;
  451. uint8_t encoded[256*sizeof(T)];
  452. HextileTile<T> tile;
  453. for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) {
  454. t.br.y = __rfbmin(pb->height(), t.tl.y + 16);
  455. for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) {
  456. t.br.x = __rfbmin(pb->width(), t.tl.x + 16);
  457. pb->getImage(buf, t);
  458. tile.newTile(buf, t.width(), t.height());
  459. int tileType = tile.getFlags();
  460. size_t encodedLen = tile.getSize();
  461. if ( (tileType & hextileRaw) != 0 ||
  462. encodedLen >= t.width() * t.height() * sizeof(T)) {
  463. os->writeU8(hextileRaw);
  464. os->writeBytes(buf, t.width() * t.height() * sizeof(T));
  465. oldBgValid = oldFgValid = false;
  466. continue;
  467. }
  468. T bg = tile.getBackground();
  469. T fg = 0;
  470. if (!oldBgValid || oldBg != bg) {
  471. tileType |= hextileBgSpecified;
  472. oldBg = bg;
  473. oldBgValid = true;
  474. }
  475. if (tileType & hextileAnySubrects) {
  476. if (tileType & hextileSubrectsColoured) {
  477. oldFgValid = false;
  478. } else {
  479. fg = tile.getForeground();
  480. if (!oldFgValid || oldFg != fg) {
  481. tileType |= hextileFgSpecified;
  482. oldFg = fg;
  483. oldFgValid = true;
  484. }
  485. }
  486. tile.encode(encoded);
  487. }
  488. os->writeU8(tileType);
  489. if (tileType & hextileBgSpecified) writePixel(os, bg);
  490. if (tileType & hextileFgSpecified) writePixel(os, fg);
  491. if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
  492. }
  493. }
  494. }