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.

TightEncoder.cxx 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
  2. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #include <rdr/OutStream.h>
  20. #include <rfb/TransImageGetter.h>
  21. #include <rfb/encodings.h>
  22. #include <rfb/ConnParams.h>
  23. #include <rfb/SMsgWriter.h>
  24. #include <rfb/TightEncoder.h>
  25. using namespace rfb;
  26. // Minimum amount of data to be compressed. This value should not be
  27. // changed, doing so will break compatibility with existing clients.
  28. #define TIGHT_MIN_TO_COMPRESS 12
  29. // Adjustable parameters.
  30. // FIXME: Get rid of #defines
  31. #define TIGHT_MAX_SPLIT_TILE_SIZE 16
  32. #define TIGHT_MIN_SPLIT_RECT_SIZE 4096
  33. #define TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
  34. //
  35. // Compression level stuff. The following array contains various
  36. // encoder parameters for each of 10 compression levels (0..9).
  37. // Last three parameters correspond to JPEG quality levels (0..9).
  38. //
  39. // NOTE: The parameters used in this encoder are the result of painstaking
  40. // research by The VirtualGL Project using RFB session captures from a variety
  41. // of both 2D and 3D applications. See http://www.VirtualGL.org for the full
  42. // reports.
  43. // NOTE: The JPEG quality and subsampling levels below were obtained
  44. // experimentally by the VirtualGL Project. They represent the approximate
  45. // average compression ratios listed below, as measured across the set of
  46. // every 10th frame in the SPECviewperf 9 benchmark suite.
  47. //
  48. // 9 = JPEG quality 100, no subsampling (ratio ~= 10:1)
  49. // [this should be lossless, except for round-off error]
  50. // 8 = JPEG quality 92, no subsampling (ratio ~= 20:1)
  51. // [this should be perceptually lossless, based on current research]
  52. // 7 = JPEG quality 86, no subsampling (ratio ~= 25:1)
  53. // 6 = JPEG quality 79, no subsampling (ratio ~= 30:1)
  54. // 5 = JPEG quality 77, 4:2:2 subsampling (ratio ~= 40:1)
  55. // 4 = JPEG quality 62, 4:2:2 subsampling (ratio ~= 50:1)
  56. // 3 = JPEG quality 42, 4:2:2 subsampling (ratio ~= 60:1)
  57. // 2 = JPEG quality 41, 4:2:0 subsampling (ratio ~= 70:1)
  58. // 1 = JPEG quality 29, 4:2:0 subsampling (ratio ~= 80:1)
  59. // 0 = JPEG quality 15, 4:2:0 subsampling (ratio ~= 100:1)
  60. const TIGHT_CONF TightEncoder::conf[10] = {
  61. { 65536, 2048, 6, 0, 0, 0, 4, 24, 15, SUBSAMP_420 }, // 0
  62. { 65536, 2048, 6, 1, 1, 1, 8, 24, 29, SUBSAMP_420 }, // 1
  63. { 65536, 2048, 8, 3, 3, 2, 24, 96, 41, SUBSAMP_420 }, // 2
  64. { 65536, 2048, 12, 5, 5, 2, 32, 96, 42, SUBSAMP_422 }, // 3
  65. { 65536, 2048, 12, 6, 7, 3, 32, 96, 62, SUBSAMP_422 }, // 4
  66. { 65536, 2048, 12, 7, 8, 4, 32, 96, 77, SUBSAMP_422 }, // 5
  67. { 65536, 2048, 16, 7, 8, 5, 32, 96, 79, SUBSAMP_NONE }, // 6
  68. { 65536, 2048, 16, 8, 9, 6, 64, 96, 86, SUBSAMP_NONE }, // 7
  69. { 65536, 2048, 24, 9, 9, 7, 64, 96, 92, SUBSAMP_NONE }, // 8
  70. { 65536, 2048, 32, 9, 9, 9, 96, 96,100, SUBSAMP_NONE } // 9
  71. };
  72. const int TightEncoder::defaultCompressLevel = 2;
  73. //
  74. // Including BPP-dependent implementation of the encoder.
  75. //
  76. #define BPP 8
  77. #include <rfb/tightEncode.h>
  78. #undef BPP
  79. #define BPP 16
  80. #include <rfb/tightEncode.h>
  81. #undef BPP
  82. #define BPP 32
  83. #include <rfb/tightEncode.h>
  84. #undef BPP
  85. TightEncoder::TightEncoder(SMsgWriter* writer_) : writer(writer_)
  86. {
  87. setCompressLevel(defaultCompressLevel);
  88. setQualityLevel(-1);
  89. }
  90. TightEncoder::~TightEncoder()
  91. {
  92. }
  93. void TightEncoder::setCompressLevel(int level)
  94. {
  95. if (level >= 0 && level <= 9) {
  96. pconf = &conf[level];
  97. } else {
  98. pconf = &conf[defaultCompressLevel];
  99. }
  100. }
  101. void TightEncoder::setQualityLevel(int level)
  102. {
  103. if (level >= 0 && level <= 9) {
  104. jpegQuality = conf[level].jpegQuality;
  105. jpegSubsampling = conf[level].jpegSubsampling;
  106. } else {
  107. jpegQuality = -1;
  108. jpegSubsampling = SUBSAMP_UNDEFINED;
  109. }
  110. }
  111. void TightEncoder::setFineQualityLevel(int quality, JPEG_SUBSAMP subsampling)
  112. {
  113. if (quality >= 1 && quality <= 100) {
  114. jpegQuality = quality;
  115. }
  116. if (subsampling >= SUBSAMP_NONE && subsampling <= SUBSAMP_GRAY) {
  117. jpegSubsampling = subsampling;
  118. }
  119. }
  120. bool TightEncoder::checkSolidTile(Rect& r, rdr::U32* colorPtr,
  121. bool needSameColor)
  122. {
  123. switch (serverpf.bpp) {
  124. case 32:
  125. return checkSolidTile32(r, colorPtr, needSameColor);
  126. case 16:
  127. return checkSolidTile16(r, colorPtr, needSameColor);
  128. default:
  129. return checkSolidTile8(r, colorPtr, needSameColor);
  130. }
  131. }
  132. void TightEncoder::findBestSolidArea(Rect& r, rdr::U32 colorValue, Rect& bestr)
  133. {
  134. int dx, dy, dw, dh;
  135. int w_prev;
  136. Rect sr;
  137. int w_best = 0, h_best = 0;
  138. bestr.tl.x = bestr.br.x = r.tl.x;
  139. bestr.tl.y = bestr.br.y = r.tl.y;
  140. w_prev = r.width();
  141. for (dy = r.tl.y; dy < r.br.y; dy += TIGHT_MAX_SPLIT_TILE_SIZE) {
  142. dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= r.br.y) ?
  143. TIGHT_MAX_SPLIT_TILE_SIZE : (r.br.y - dy);
  144. dw = (w_prev > TIGHT_MAX_SPLIT_TILE_SIZE) ?
  145. TIGHT_MAX_SPLIT_TILE_SIZE : w_prev;
  146. sr.setXYWH(r.tl.x, dy, dw, dh);
  147. if (!checkSolidTile(sr, &colorValue, true))
  148. break;
  149. for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
  150. dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= r.tl.x + w_prev) ?
  151. TIGHT_MAX_SPLIT_TILE_SIZE : (r.tl.x + w_prev - dx);
  152. sr.setXYWH(dx, dy, dw, dh);
  153. if (!checkSolidTile(sr, &colorValue, true))
  154. break;
  155. dx += dw;
  156. }
  157. w_prev = dx - r.tl.x;
  158. if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
  159. w_best = w_prev;
  160. h_best = dy + dh - r.tl.y;
  161. }
  162. }
  163. bestr.br.x = bestr.tl.x + w_best;
  164. bestr.br.y = bestr.tl.y + h_best;
  165. }
  166. void TightEncoder::extendSolidArea(const Rect& r, rdr::U32 colorValue,
  167. Rect& er)
  168. {
  169. int cx, cy;
  170. Rect sr;
  171. // Try to extend the area upwards.
  172. for (cy = er.tl.y - 1; ; cy--) {
  173. sr.setXYWH(er.tl.x, cy, er.width(), 1);
  174. if (cy < r.tl.y || !checkSolidTile(sr, &colorValue, true))
  175. break;
  176. }
  177. er.tl.y = cy + 1;
  178. // ... downwards.
  179. for (cy = er.br.y; ; cy++) {
  180. sr.setXYWH(er.tl.x, cy, er.width(), 1);
  181. if (cy >= r.br.y || !checkSolidTile(sr, &colorValue, true))
  182. break;
  183. }
  184. er.br.y = cy;
  185. // ... to the left.
  186. for (cx = er.tl.x - 1; ; cx--) {
  187. sr.setXYWH(cx, er.tl.y, 1, er.height());
  188. if (cx < r.tl.x || !checkSolidTile(sr, &colorValue, true))
  189. break;
  190. }
  191. er.tl.x = cx + 1;
  192. // ... to the right.
  193. for (cx = er.br.x; ; cx++) {
  194. sr.setXYWH(cx, er.tl.y, 1, er.height());
  195. if (cx >= r.br.x || !checkSolidTile(sr, &colorValue, true))
  196. break;
  197. }
  198. er.br.x = cx;
  199. }
  200. int TightEncoder::getNumRects(const Rect &r)
  201. {
  202. ConnParams* cp = writer->getConnParams();
  203. const unsigned int w = r.width();
  204. const unsigned int h = r.height();
  205. // If last rect. encoding is enabled, we can use the higher-performance
  206. // code that pre-computes solid rectangles. In that case, we don't care
  207. // about the rectangle count.
  208. if (cp->supportsLastRect && w * h >= TIGHT_MIN_SPLIT_RECT_SIZE)
  209. return 0;
  210. // Will this rectangle split into subrects?
  211. bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
  212. if (!rectTooBig)
  213. return 1;
  214. // Compute max sub-rectangle size.
  215. const unsigned int subrectMaxWidth =
  216. (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
  217. const unsigned int subrectMaxHeight =
  218. pconf->maxRectSize / subrectMaxWidth;
  219. // Return the number of subrects.
  220. return (((w - 1) / pconf->maxRectWidth + 1) *
  221. ((h - 1) / subrectMaxHeight + 1));
  222. }
  223. void TightEncoder::sendRectSimple(const Rect& r)
  224. {
  225. // Shortcuts to rectangle coordinates and dimensions.
  226. const int x = r.tl.x;
  227. const int y = r.tl.y;
  228. const unsigned int w = r.width();
  229. const unsigned int h = r.height();
  230. // Encode small rects as is.
  231. bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
  232. if (!rectTooBig) {
  233. writeSubrect(r);
  234. return;
  235. }
  236. // Compute max sub-rectangle size.
  237. const unsigned int subrectMaxWidth =
  238. (w > pconf->maxRectWidth) ? pconf->maxRectWidth : w;
  239. const unsigned int subrectMaxHeight =
  240. pconf->maxRectSize / subrectMaxWidth;
  241. // Split big rects into separately encoded subrects.
  242. Rect sr;
  243. unsigned int dx, dy, sw, sh;
  244. for (dy = 0; dy < h; dy += subrectMaxHeight) {
  245. for (dx = 0; dx < w; dx += pconf->maxRectWidth) {
  246. sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
  247. sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
  248. sr.setXYWH(x + dx, y + dy, sw, sh);
  249. writeSubrect(sr);
  250. }
  251. }
  252. }
  253. bool TightEncoder::writeRect(const Rect& _r, TransImageGetter* _ig,
  254. Rect* actual)
  255. {
  256. ig = _ig;
  257. serverpf = ig->getPixelBuffer()->getPF();
  258. ConnParams* cp = writer->getConnParams();
  259. clientpf = cp->pf();
  260. // Shortcuts to rectangle coordinates and dimensions.
  261. Rect r = _r;
  262. int x = r.tl.x;
  263. int y = r.tl.y;
  264. int w = r.width();
  265. int h = r.height();
  266. // Encode small rects as is.
  267. if (!cp->supportsLastRect || w * h < TIGHT_MIN_SPLIT_RECT_SIZE) {
  268. sendRectSimple(r);
  269. return true;
  270. }
  271. // Split big rects into separately encoded subrects.
  272. Rect sr, bestr;
  273. int dx, dy, dw, dh;
  274. rdr::U32 colorValue;
  275. int maxRectWidth = pconf->maxRectWidth;
  276. int nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
  277. int nMaxRows = pconf->maxRectSize / nMaxWidth;
  278. // Try to find large solid-color areas and send them separately.
  279. for (dy = y; dy < y + h; dy += TIGHT_MAX_SPLIT_TILE_SIZE) {
  280. // If a rectangle becomes too large, send its upper part now.
  281. if (dy - y >= nMaxRows) {
  282. sr.setXYWH(x, y, w, nMaxRows);
  283. sendRectSimple(sr);
  284. r.tl.y += nMaxRows;
  285. y = r.tl.y;
  286. h = r.height();
  287. }
  288. dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= y + h) ?
  289. TIGHT_MAX_SPLIT_TILE_SIZE : (y + h - dy);
  290. for (dx = x; dx < x + w; dx += TIGHT_MAX_SPLIT_TILE_SIZE) {
  291. dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= x + w) ?
  292. TIGHT_MAX_SPLIT_TILE_SIZE : (x + w - dx);
  293. sr.setXYWH(dx, dy, dw, dh);
  294. if (checkSolidTile(sr, &colorValue, false)) {
  295. if (jpegSubsampling == SUBSAMP_GRAY && jpegQuality != -1) {
  296. Colour rgb;
  297. serverpf.rgbFromPixel(colorValue, NULL, &rgb);
  298. rdr::U32 lum = ((257 * rgb.r) + (504 * rgb.g) + (98 * rgb.b)
  299. + 16500) / 1000;
  300. colorValue = lum + (lum << 8) + (lum << 16);
  301. }
  302. // Get dimensions of solid-color area.
  303. sr.setXYWH(dx, dy, r.br.x - dx, r.br.y - dy);
  304. findBestSolidArea(sr, colorValue, bestr);
  305. // Make sure a solid rectangle is large enough
  306. // (or the whole rectangle is of the same color).
  307. if (bestr.area() != r.area()
  308. && bestr.area() < TIGHT_MIN_SOLID_SUBRECT_SIZE)
  309. continue;
  310. // Try to extend solid rectangle to maximum size.
  311. extendSolidArea(r, colorValue, bestr);
  312. // Send rectangles at top and left to solid-color area.
  313. if (bestr.tl.y != y) {
  314. sr.setXYWH(x, y, w, bestr.tl.y - y);
  315. sendRectSimple(sr);
  316. }
  317. if (bestr.tl.x != x) {
  318. sr.setXYWH(x, bestr.tl.y, bestr.tl.x - x, bestr.height());
  319. writeRect(sr, _ig, NULL);
  320. }
  321. // Send solid-color rectangle.
  322. writeSubrect(bestr, true);
  323. // Send remaining rectangles (at right and bottom).
  324. if (bestr.br.x != r.br.x) {
  325. sr.setXYWH(bestr.br.x, bestr.tl.y, r.br.x - bestr.br.x,
  326. bestr.height());
  327. writeRect(sr, _ig, NULL);
  328. }
  329. if (bestr.br.y != r.br.y) {
  330. sr.setXYWH(x, bestr.br.y, w, r.br.y - bestr.br.y);
  331. writeRect(sr, _ig, NULL);
  332. }
  333. return true;
  334. }
  335. }
  336. }
  337. // No suitable solid-color rectangles found.
  338. sendRectSimple(r);
  339. return true;
  340. }
  341. void TightEncoder::writeSubrect(const Rect& r, bool forceSolid)
  342. {
  343. mos.clear();
  344. switch (clientpf.bpp) {
  345. case 8:
  346. tightEncode8(r, &mos, forceSolid); break;
  347. case 16:
  348. tightEncode16(r, &mos, forceSolid); break;
  349. case 32:
  350. tightEncode32(r, &mos, forceSolid); break;
  351. }
  352. writer->startRect(r, encodingTight);
  353. rdr::OutStream* os = writer->getOutStream();
  354. os->writeBytes(mos.data(), mos.length());
  355. writer->endRect();
  356. }