Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

EncodeManager.cxx 27KB

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