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.

EncodeManager.cxx 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
  2. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  3. * Copyright 2014 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. #include <rfb/EncodeManager.h>
  21. #include <rfb/Encoder.h>
  22. #include <rfb/Palette.h>
  23. #include <rfb/SConnection.h>
  24. #include <rfb/SMsgWriter.h>
  25. #include <rfb/UpdateTracker.h>
  26. #include <rfb/LogWriter.h>
  27. #include <rfb/RawEncoder.h>
  28. #include <rfb/RREEncoder.h>
  29. #include <rfb/HextileEncoder.h>
  30. #include <rfb/ZRLEEncoder.h>
  31. #include <rfb/TightEncoder.h>
  32. #include <rfb/TightJPEGEncoder.h>
  33. using namespace rfb;
  34. static LogWriter vlog("EncodeManager");
  35. // Split each rectangle into smaller ones no larger than this area,
  36. // and no wider than this width.
  37. static const int SubRectMaxArea = 65536;
  38. static const int SubRectMaxWidth = 2048;
  39. // The size in pixels of either side of each block tested when looking
  40. // for solid blocks.
  41. static const int SolidSearchBlock = 16;
  42. // Don't bother with blocks smaller than this
  43. static const int SolidBlockMinArea = 2048;
  44. namespace rfb {
  45. enum EncoderClass {
  46. encoderRaw,
  47. encoderRRE,
  48. encoderHextile,
  49. encoderTight,
  50. encoderTightJPEG,
  51. encoderZRLE,
  52. encoderClassMax,
  53. };
  54. enum EncoderType {
  55. encoderSolid,
  56. encoderBitmap,
  57. encoderBitmapRLE,
  58. encoderIndexed,
  59. encoderIndexedRLE,
  60. encoderFullColour,
  61. encoderTypeMax,
  62. };
  63. struct RectInfo {
  64. int rleRuns;
  65. Palette palette;
  66. };
  67. };
  68. static const char *encoderClassName(EncoderClass klass)
  69. {
  70. switch (klass) {
  71. case encoderRaw:
  72. return "Raw";
  73. case encoderRRE:
  74. return "RRE";
  75. case encoderHextile:
  76. return "Hextile";
  77. case encoderTight:
  78. return "Tight";
  79. case encoderTightJPEG:
  80. return "Tight (JPEG)";
  81. case encoderZRLE:
  82. return "ZRLE";
  83. case encoderClassMax:
  84. break;
  85. }
  86. return "Unknown Encoder Class";
  87. }
  88. static const char *encoderTypeName(EncoderType type)
  89. {
  90. switch (type) {
  91. case encoderSolid:
  92. return "Solid";
  93. case encoderBitmap:
  94. return "Bitmap";
  95. case encoderBitmapRLE:
  96. return "Bitmap RLE";
  97. case encoderIndexed:
  98. return "Indexed";
  99. case encoderIndexedRLE:
  100. return "Indexed RLE";
  101. case encoderFullColour:
  102. return "Full Colour";
  103. case encoderTypeMax:
  104. break;
  105. }
  106. return "Unknown Encoder Type";
  107. }
  108. EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
  109. {
  110. StatsVector::iterator iter;
  111. encoders.resize(encoderClassMax, NULL);
  112. activeEncoders.resize(encoderTypeMax, encoderRaw);
  113. encoders[encoderRaw] = new RawEncoder(conn);
  114. encoders[encoderRRE] = new RREEncoder(conn);
  115. encoders[encoderHextile] = new HextileEncoder(conn);
  116. encoders[encoderTight] = new TightEncoder(conn);
  117. encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
  118. encoders[encoderZRLE] = new ZRLEEncoder(conn);
  119. updates = 0;
  120. stats.resize(encoderClassMax);
  121. for (iter = stats.begin();iter != stats.end();++iter) {
  122. StatsVector::value_type::iterator iter2;
  123. iter->resize(encoderTypeMax);
  124. for (iter2 = iter->begin();iter2 != iter->end();++iter2)
  125. memset(&*iter2, 0, sizeof(EncoderStats));
  126. }
  127. }
  128. EncodeManager::~EncodeManager()
  129. {
  130. std::vector<Encoder*>::iterator iter;
  131. logStats();
  132. for (iter = encoders.begin();iter != encoders.end();iter++)
  133. delete *iter;
  134. }
  135. void EncodeManager::logStats()
  136. {
  137. size_t i, j;
  138. unsigned rects;
  139. unsigned long long pixels, bytes, equivalent;
  140. double ratio;
  141. char a[1024], b[1024];
  142. rects = 0;
  143. pixels = bytes = equivalent = 0;
  144. vlog.info("Framebuffer updates: %u", updates);
  145. for (i = 0;i < stats.size();i++) {
  146. // Did this class do anything at all?
  147. for (j = 0;j < stats[i].size();j++) {
  148. if (stats[i][j].rects != 0)
  149. break;
  150. }
  151. if (j == stats[i].size())
  152. continue;
  153. vlog.info(" %s:", encoderClassName((EncoderClass)i));
  154. for (j = 0;j < stats[i].size();j++) {
  155. if (stats[i][j].rects == 0)
  156. continue;
  157. rects += stats[i][j].rects;
  158. pixels += stats[i][j].pixels;
  159. bytes += stats[i][j].bytes;
  160. equivalent += stats[i][j].equivalent;
  161. ratio = (double)stats[i][j].equivalent / stats[i][j].bytes;
  162. siPrefix(stats[i][j].rects, "rects", a, sizeof(a));
  163. siPrefix(stats[i][j].pixels, "pixels", b, sizeof(b));
  164. vlog.info(" %s: %s, %s", encoderTypeName((EncoderType)j), a, b);
  165. iecPrefix(stats[i][j].bytes, "B", a, sizeof(a));
  166. vlog.info(" %*s %s (1:%g ratio)",
  167. (int)strlen(encoderTypeName((EncoderType)j)), "",
  168. a, ratio);
  169. }
  170. }
  171. ratio = (double)equivalent / bytes;
  172. siPrefix(rects, "rects", a, sizeof(a));
  173. siPrefix(pixels, "pixels", b, sizeof(b));
  174. vlog.info(" Total: %s, %s", a, b);
  175. iecPrefix(bytes, "B", a, sizeof(a));
  176. vlog.info(" %s (1:%g ratio)", a, ratio);
  177. }
  178. bool EncodeManager::supported(int encoding)
  179. {
  180. switch (encoding) {
  181. case encodingRaw:
  182. case encodingRRE:
  183. case encodingHextile:
  184. case encodingZRLE:
  185. case encodingTight:
  186. return true;
  187. default:
  188. return false;
  189. }
  190. }
  191. void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
  192. const RenderedCursor* renderedCursor)
  193. {
  194. int nRects;
  195. Region changed;
  196. updates++;
  197. prepareEncoders();
  198. if (conn->cp.supportsLastRect)
  199. nRects = 0xFFFF;
  200. else {
  201. nRects = ui.copied.numRects();
  202. nRects += computeNumRects(ui.changed);
  203. if (renderedCursor != NULL)
  204. nRects += 1;
  205. }
  206. conn->writer()->writeFramebufferUpdateStart(nRects);
  207. writeCopyRects(ui);
  208. /*
  209. * We start by searching for solid rects, which are then removed
  210. * from the changed region.
  211. */
  212. changed.copyFrom(ui.changed);
  213. if (conn->cp.supportsLastRect)
  214. writeSolidRects(&changed, pb);
  215. writeRects(changed, pb);
  216. if (renderedCursor != NULL) {
  217. Rect renderedCursorRect;
  218. renderedCursorRect = renderedCursor->getEffectiveRect();
  219. writeSubRect(renderedCursorRect, renderedCursor);
  220. }
  221. conn->writer()->writeFramebufferUpdateEnd();
  222. }
  223. void EncodeManager::prepareEncoders()
  224. {
  225. enum EncoderClass solid, bitmap, bitmapRLE;
  226. enum EncoderClass indexed, indexedRLE, fullColour;
  227. rdr::S32 preferred;
  228. std::vector<int>::iterator iter;
  229. solid = bitmap = bitmapRLE = encoderRaw;
  230. indexed = indexedRLE = fullColour = encoderRaw;
  231. // Try to respect the client's wishes
  232. preferred = conn->getPreferredEncoding();
  233. switch (preferred) {
  234. case encodingRRE:
  235. // Horrible for anything high frequency and/or lots of colours
  236. bitmapRLE = indexedRLE = encoderRRE;
  237. break;
  238. case encodingHextile:
  239. // Slightly less horrible
  240. bitmapRLE = indexedRLE = fullColour = encoderHextile;
  241. break;
  242. case encodingTight:
  243. if (encoders[encoderTightJPEG]->isSupported() &&
  244. (conn->cp.pf().bpp >= 16))
  245. fullColour = encoderTightJPEG;
  246. else
  247. fullColour = encoderTight;
  248. indexed = indexedRLE = encoderTight;
  249. bitmap = bitmapRLE = encoderTight;
  250. break;
  251. case encodingZRLE:
  252. fullColour = encoderZRLE;
  253. bitmapRLE = indexedRLE = encoderZRLE;
  254. bitmap = indexed = encoderZRLE;
  255. break;
  256. }
  257. // Any encoders still unassigned?
  258. if (fullColour == encoderRaw) {
  259. if (encoders[encoderTightJPEG]->isSupported() &&
  260. (conn->cp.pf().bpp >= 16))
  261. fullColour = encoderTightJPEG;
  262. else if (encoders[encoderZRLE]->isSupported())
  263. fullColour = encoderZRLE;
  264. else if (encoders[encoderTight]->isSupported())
  265. fullColour = encoderTight;
  266. else if (encoders[encoderHextile]->isSupported())
  267. fullColour = encoderHextile;
  268. }
  269. if (indexed == encoderRaw) {
  270. if (encoders[encoderZRLE]->isSupported())
  271. indexed = encoderZRLE;
  272. else if (encoders[encoderTight]->isSupported())
  273. indexed = encoderTight;
  274. else if (encoders[encoderHextile]->isSupported())
  275. indexed = encoderHextile;
  276. }
  277. if (indexedRLE == encoderRaw)
  278. indexedRLE = indexed;
  279. if (bitmap == encoderRaw)
  280. bitmap = indexed;
  281. if (bitmapRLE == encoderRaw)
  282. bitmapRLE = bitmap;
  283. if (solid == encoderRaw) {
  284. if (encoders[encoderTight]->isSupported())
  285. solid = encoderTight;
  286. else if (encoders[encoderRRE]->isSupported())
  287. solid = encoderRRE;
  288. else if (encoders[encoderZRLE]->isSupported())
  289. solid = encoderZRLE;
  290. else if (encoders[encoderHextile]->isSupported())
  291. solid = encoderHextile;
  292. }
  293. // JPEG is the only encoder that can reduce things to grayscale
  294. if ((conn->cp.subsampling == subsampleGray) &&
  295. encoders[encoderTightJPEG]->isSupported()) {
  296. solid = bitmap = bitmapRLE = encoderTightJPEG;
  297. indexed = indexedRLE = fullColour = encoderTightJPEG;
  298. }
  299. activeEncoders[encoderSolid] = solid;
  300. activeEncoders[encoderBitmap] = bitmap;
  301. activeEncoders[encoderBitmapRLE] = bitmapRLE;
  302. activeEncoders[encoderIndexed] = indexed;
  303. activeEncoders[encoderIndexedRLE] = indexedRLE;
  304. activeEncoders[encoderFullColour] = fullColour;
  305. for (iter = activeEncoders.begin(); iter != activeEncoders.end(); ++iter) {
  306. Encoder *encoder;
  307. encoder = encoders[*iter];
  308. encoder->setCompressLevel(conn->cp.compressLevel);
  309. encoder->setQualityLevel(conn->cp.qualityLevel);
  310. encoder->setFineQualityLevel(conn->cp.fineQualityLevel,
  311. conn->cp.subsampling);
  312. }
  313. }
  314. int EncodeManager::computeNumRects(const Region& changed)
  315. {
  316. int numRects;
  317. std::vector<Rect> rects;
  318. std::vector<Rect>::const_iterator rect;
  319. numRects = 0;
  320. changed.get_rects(&rects);
  321. for (rect = rects.begin(); rect != rects.end(); ++rect) {
  322. int w, h, sw, sh;
  323. w = rect->width();
  324. h = rect->height();
  325. // No split necessary?
  326. if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
  327. numRects += 1;
  328. continue;
  329. }
  330. if (w <= SubRectMaxWidth)
  331. sw = w;
  332. else
  333. sw = SubRectMaxWidth;
  334. sh = SubRectMaxArea / sw;
  335. // ceil(w/sw) * ceil(h/sh)
  336. numRects += (((w - 1)/sw) + 1) * (((h - 1)/sh) + 1);
  337. }
  338. return numRects;
  339. }
  340. Encoder *EncodeManager::startRect(const Rect& rect, int type)
  341. {
  342. Encoder *encoder;
  343. int klass, equiv;
  344. activeType = type;
  345. klass = activeEncoders[activeType];
  346. beforeLength = conn->getOutStream()->length();
  347. stats[klass][activeType].rects++;
  348. stats[klass][activeType].pixels += rect.area();
  349. equiv = 12 + rect.area() * conn->cp.pf().bpp/8;
  350. stats[klass][activeType].equivalent += equiv;
  351. encoder = encoders[klass];
  352. conn->writer()->startRect(rect, encoder->encoding);
  353. return encoder;
  354. }
  355. void EncodeManager::endRect()
  356. {
  357. int klass;
  358. int length;
  359. conn->writer()->endRect();
  360. length = conn->getOutStream()->length() - beforeLength;
  361. klass = activeEncoders[activeType];
  362. stats[klass][activeType].bytes += length;
  363. }
  364. void EncodeManager::writeCopyRects(const UpdateInfo& ui)
  365. {
  366. std::vector<Rect> rects;
  367. std::vector<Rect>::const_iterator rect;
  368. ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
  369. for (rect = rects.begin(); rect != rects.end(); ++rect) {
  370. conn->writer()->writeCopyRect(*rect, rect->tl.x - ui.copy_delta.x,
  371. rect->tl.y - ui.copy_delta.y);
  372. }
  373. }
  374. void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
  375. {
  376. std::vector<Rect> rects;
  377. std::vector<Rect>::const_iterator rect;
  378. changed->get_rects(&rects);
  379. for (rect = rects.begin(); rect != rects.end(); ++rect)
  380. findSolidRect(*rect, changed, pb);
  381. }
  382. void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
  383. const PixelBuffer* pb)
  384. {
  385. Rect sr;
  386. int dx, dy, dw, dh;
  387. // We start by finding a solid 16x16 block
  388. for (dy = rect.tl.y; dy < rect.br.y; dy += SolidSearchBlock) {
  389. dh = SolidSearchBlock;
  390. if (dy + dh > rect.br.y)
  391. dh = rect.br.y - dy;
  392. for (dx = rect.tl.x; dx < rect.br.x; dx += SolidSearchBlock) {
  393. // We define it like this to guarantee alignment
  394. rdr::U32 _buffer;
  395. rdr::U8* colourValue = (rdr::U8*)&_buffer;
  396. dw = SolidSearchBlock;
  397. if (dx + dw > rect.br.x)
  398. dw = rect.br.x - dx;
  399. pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));
  400. sr.setXYWH(dx, dy, dw, dh);
  401. if (checkSolidTile(sr, colourValue, pb)) {
  402. Rect erb, erp;
  403. Encoder *encoder;
  404. // We then try extending the area by adding more blocks
  405. // in both directions and pick the combination that gives
  406. // the largest area.
  407. sr.setXYWH(dx, dy, rect.br.x - dx, rect.br.y - dy);
  408. extendSolidAreaByBlock(sr, colourValue, pb, &erb);
  409. // Did we end up getting the entire rectangle?
  410. if (erb.equals(rect))
  411. erp = erb;
  412. else {
  413. // Don't bother with sending tiny rectangles
  414. if (erb.area() < SolidBlockMinArea)
  415. continue;
  416. // Extend the area again, but this time one pixel
  417. // row/column at a time.
  418. extendSolidAreaByPixel(rect, erb, colourValue, pb, &erp);
  419. }
  420. // Send solid-color rectangle.
  421. encoder = startRect(erp, encoderSolid);
  422. if (encoder->flags & EncoderUseNativePF) {
  423. encoder->writeSolidRect(erp.width(), erp.height(),
  424. pb->getPF(), colourValue);
  425. } else {
  426. rdr::U32 _buffer2;
  427. rdr::U8* converted = (rdr::U8*)&_buffer2;
  428. conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
  429. colourValue, 1);
  430. encoder->writeSolidRect(erp.width(), erp.height(),
  431. conn->cp.pf(), converted);
  432. }
  433. endRect();
  434. changed->assign_subtract(Region(erp));
  435. // Search remaining areas by recursion
  436. // FIXME: Is this the best way to divide things up?
  437. // Left? (Note that we've already searched a SolidSearchBlock
  438. // pixels high strip here)
  439. if ((erp.tl.x != rect.tl.x) && (erp.height() > SolidSearchBlock)) {
  440. sr.setXYWH(rect.tl.x, erp.tl.y + SolidSearchBlock,
  441. erp.tl.x - rect.tl.x, erp.height() - SolidSearchBlock);
  442. findSolidRect(sr, changed, pb);
  443. }
  444. // Right?
  445. if (erp.br.x != rect.br.x) {
  446. sr.setXYWH(erp.br.x, erp.tl.y, rect.br.x - erp.br.x, erp.height());
  447. findSolidRect(sr, changed, pb);
  448. }
  449. // Below?
  450. if (erp.br.y != rect.br.y) {
  451. sr.setXYWH(rect.tl.x, erp.br.y, rect.width(), rect.br.y - erp.br.y);
  452. findSolidRect(sr, changed, pb);
  453. }
  454. return;
  455. }
  456. }
  457. }
  458. }
  459. void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb)
  460. {
  461. std::vector<Rect> rects;
  462. std::vector<Rect>::const_iterator rect;
  463. changed.get_rects(&rects);
  464. for (rect = rects.begin(); rect != rects.end(); ++rect) {
  465. int w, h, sw, sh;
  466. Rect sr;
  467. w = rect->width();
  468. h = rect->height();
  469. // No split necessary?
  470. if (((w*h) < SubRectMaxArea) && (w < SubRectMaxWidth)) {
  471. writeSubRect(*rect, pb);
  472. continue;
  473. }
  474. if (w <= SubRectMaxWidth)
  475. sw = w;
  476. else
  477. sw = SubRectMaxWidth;
  478. sh = SubRectMaxArea / sw;
  479. for (sr.tl.y = rect->tl.y; sr.tl.y < rect->br.y; sr.tl.y += sh) {
  480. sr.br.y = sr.tl.y + sh;
  481. if (sr.br.y > rect->br.y)
  482. sr.br.y = rect->br.y;
  483. for (sr.tl.x = rect->tl.x; sr.tl.x < rect->br.x; sr.tl.x += sw) {
  484. sr.br.x = sr.tl.x + sw;
  485. if (sr.br.x > rect->br.x)
  486. sr.br.x = rect->br.x;
  487. writeSubRect(sr, pb);
  488. }
  489. }
  490. }
  491. }
  492. void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
  493. {
  494. PixelBuffer *ppb;
  495. Encoder *encoder;
  496. struct RectInfo info;
  497. unsigned int divisor, maxColours;
  498. bool useRLE;
  499. EncoderType type;
  500. // FIXME: This is roughly the algorithm previously used by the Tight
  501. // encoder. It seems a bit backwards though, that higher
  502. // compression setting means spending less effort in building
  503. // a palette. It might be that they figured the increase in
  504. // zlib setting compensated for the loss.
  505. if (conn->cp.compressLevel == -1)
  506. divisor = 2 * 8;
  507. else
  508. divisor = conn->cp.compressLevel * 8;
  509. if (divisor < 4)
  510. divisor = 4;
  511. maxColours = rect.area()/divisor;
  512. // Special exception inherited from the Tight encoder
  513. if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
  514. if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2))
  515. maxColours = 24;
  516. else
  517. maxColours = 96;
  518. }
  519. if (maxColours < 2)
  520. maxColours = 2;
  521. encoder = encoders[activeEncoders[encoderIndexedRLE]];
  522. if (maxColours > encoder->maxPaletteSize)
  523. maxColours = encoder->maxPaletteSize;
  524. encoder = encoders[activeEncoders[encoderIndexed]];
  525. if (maxColours > encoder->maxPaletteSize)
  526. maxColours = encoder->maxPaletteSize;
  527. ppb = preparePixelBuffer(rect, pb, true);
  528. if (!analyseRect(ppb, &info, maxColours))
  529. info.palette.clear();
  530. // Different encoders might have different RLE overhead, but
  531. // here we do a guess at RLE being the better choice if reduces
  532. // the pixel count by 50%.
  533. useRLE = info.rleRuns <= (rect.area() * 2);
  534. switch (info.palette.size()) {
  535. case 0:
  536. type = encoderFullColour;
  537. break;
  538. case 1:
  539. type = encoderSolid;
  540. break;
  541. case 2:
  542. if (useRLE)
  543. type = encoderBitmapRLE;
  544. else
  545. type = encoderBitmap;
  546. break;
  547. default:
  548. if (useRLE)
  549. type = encoderIndexedRLE;
  550. else
  551. type = encoderIndexed;
  552. }
  553. encoder = startRect(rect, type);
  554. if (encoder->flags & EncoderUseNativePF)
  555. ppb = preparePixelBuffer(rect, pb, false);
  556. encoder->writeRect(ppb, info.palette);
  557. endRect();
  558. }
  559. bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,
  560. const PixelBuffer *pb)
  561. {
  562. switch (pb->getPF().bpp) {
  563. case 32:
  564. return checkSolidTile(r, *(const rdr::U32*)colourValue, pb);
  565. case 16:
  566. return checkSolidTile(r, *(const rdr::U16*)colourValue, pb);
  567. default:
  568. return checkSolidTile(r, *(const rdr::U8*)colourValue, pb);
  569. }
  570. }
  571. void EncodeManager::extendSolidAreaByBlock(const Rect& r,
  572. const rdr::U8* colourValue,
  573. const PixelBuffer *pb, Rect* er)
  574. {
  575. int dx, dy, dw, dh;
  576. int w_prev;
  577. Rect sr;
  578. int w_best = 0, h_best = 0;
  579. w_prev = r.width();
  580. // We search width first, back off when we hit a different colour,
  581. // and restart with a larger height. We keep track of the
  582. // width/height combination that gives us the largest area.
  583. for (dy = r.tl.y; dy < r.br.y; dy += SolidSearchBlock) {
  584. dh = SolidSearchBlock;
  585. if (dy + dh > r.br.y)
  586. dh = r.br.y - dy;
  587. // We test one block here outside the x loop in order to break
  588. // the y loop right away.
  589. dw = SolidSearchBlock;
  590. if (dw > w_prev)
  591. dw = w_prev;
  592. sr.setXYWH(r.tl.x, dy, dw, dh);
  593. if (!checkSolidTile(sr, colourValue, pb))
  594. break;
  595. for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
  596. dw = SolidSearchBlock;
  597. if (dx + dw > r.tl.x + w_prev)
  598. dw = r.tl.x + w_prev - dx;
  599. sr.setXYWH(dx, dy, dw, dh);
  600. if (!checkSolidTile(sr, colourValue, pb))
  601. break;
  602. dx += dw;
  603. }
  604. w_prev = dx - r.tl.x;
  605. if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) {
  606. w_best = w_prev;
  607. h_best = dy + dh - r.tl.y;
  608. }
  609. }
  610. er->tl.x = r.tl.x;
  611. er->tl.y = r.tl.y;
  612. er->br.x = er->tl.x + w_best;
  613. er->br.y = er->tl.y + h_best;
  614. }
  615. void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr,
  616. const rdr::U8* colourValue,
  617. const PixelBuffer *pb, Rect* er)
  618. {
  619. int cx, cy;
  620. Rect tr;
  621. // Try to extend the area upwards.
  622. for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) {
  623. tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
  624. if (!checkSolidTile(tr, colourValue, pb))
  625. break;
  626. }
  627. er->tl.y = cy + 1;
  628. // ... downwards.
  629. for (cy = sr.br.y; cy < r.br.y; cy++) {
  630. tr.setXYWH(sr.tl.x, cy, sr.width(), 1);
  631. if (!checkSolidTile(tr, colourValue, pb))
  632. break;
  633. }
  634. er->br.y = cy;
  635. // ... to the left.
  636. for (cx = sr.tl.x - 1; cx >= r.tl.x; cx--) {
  637. tr.setXYWH(cx, er->tl.y, 1, er->height());
  638. if (!checkSolidTile(tr, colourValue, pb))
  639. break;
  640. }
  641. er->tl.x = cx + 1;
  642. // ... to the right.
  643. for (cx = sr.br.x; cx < r.br.x; cx++) {
  644. tr.setXYWH(cx, er->tl.y, 1, er->height());
  645. if (!checkSolidTile(tr, colourValue, pb))
  646. break;
  647. }
  648. er->br.x = cx;
  649. }
  650. PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect,
  651. const PixelBuffer *pb,
  652. bool convert)
  653. {
  654. const rdr::U8* buffer;
  655. int stride;
  656. // Do wo need to convert the data?
  657. if (convert && !conn->cp.pf().equal(pb->getPF())) {
  658. convertedPixelBuffer.setPF(conn->cp.pf());
  659. convertedPixelBuffer.setSize(rect.width(), rect.height());
  660. buffer = pb->getBuffer(rect, &stride);
  661. convertedPixelBuffer.imageRect(pb->getPF(),
  662. convertedPixelBuffer.getRect(),
  663. buffer, stride);
  664. return &convertedPixelBuffer;
  665. }
  666. // Otherwise we still need to shift the coordinates. We have our own
  667. // abusive subclass of FullFramePixelBuffer for this.
  668. buffer = pb->getBuffer(rect, &stride);
  669. offsetPixelBuffer.update(pb->getPF(), rect.width(), rect.height(),
  670. buffer, stride);
  671. return &offsetPixelBuffer;
  672. }
  673. bool EncodeManager::analyseRect(const PixelBuffer *pb,
  674. struct RectInfo *info, int maxColours)
  675. {
  676. const rdr::U8* buffer;
  677. int stride;
  678. buffer = pb->getBuffer(pb->getRect(), &stride);
  679. switch (pb->getPF().bpp) {
  680. case 32:
  681. return analyseRect(pb->width(), pb->height(),
  682. (const rdr::U32*)buffer, stride,
  683. info, maxColours);
  684. case 16:
  685. return analyseRect(pb->width(), pb->height(),
  686. (const rdr::U16*)buffer, stride,
  687. info, maxColours);
  688. default:
  689. return analyseRect(pb->width(), pb->height(),
  690. (const rdr::U8*)buffer, stride,
  691. info, maxColours);
  692. }
  693. }
  694. void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf,
  695. int width, int height,
  696. const rdr::U8* data_,
  697. int stride_)
  698. {
  699. format = pf;
  700. width_ = width;
  701. height_ = height;
  702. // Forced cast. We never write anything though, so it should be safe.
  703. data = (rdr::U8*)data_;
  704. stride = stride_;
  705. }
  706. // Preprocessor generated, optimised methods
  707. #define BPP 8
  708. #include "EncodeManagerBPP.cxx"
  709. #undef BPP
  710. #define BPP 16
  711. #include "EncodeManagerBPP.cxx"
  712. #undef BPP
  713. #define BPP 32
  714. #include "EncodeManagerBPP.cxx"
  715. #undef BPP