123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
- * Copyright 2004-2005 Cendio AB.
- * Copyright 2009-2022 Pierre Ossman for Cendio AB
- * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
- #include <assert.h>
-
- #include <vector>
-
- #include <rdr/InStream.h>
- #include <rdr/MemInStream.h>
- #include <rdr/OutStream.h>
-
- #include <rfb/ServerParams.h>
- #include <rfb/Exception.h>
- #include <rfb/PixelBuffer.h>
- #include <rfb/TightConstants.h>
- #include <rfb/TightDecoder.h>
-
- using namespace rfb;
-
- static const int TIGHT_MAX_WIDTH = 2048;
- static const int TIGHT_MIN_TO_COMPRESS = 12;
-
- TightDecoder::TightDecoder() : Decoder(DecoderPartiallyOrdered)
- {
- }
-
- TightDecoder::~TightDecoder()
- {
- }
-
- bool TightDecoder::readRect(const Rect& r, rdr::InStream* is,
- const ServerParams& server, rdr::OutStream* os)
- {
- uint8_t comp_ctl;
-
- if (!is->hasData(1))
- return false;
-
- is->setRestorePoint();
-
- comp_ctl = is->readU8();
- os->writeU8(comp_ctl);
-
- comp_ctl >>= 4;
-
- // "Fill" compression type.
- if (comp_ctl == tightFill) {
- if (server.pf().is888()) {
- if (!is->hasDataOrRestore(3))
- return false;
- os->copyBytes(is, 3);
- } else {
- if (!is->hasDataOrRestore(server.pf().bpp/8))
- return false;
- os->copyBytes(is, server.pf().bpp/8);
- }
- is->clearRestorePoint();
- return true;
- }
-
- // "JPEG" compression type.
- if (comp_ctl == tightJpeg) {
- uint32_t len;
-
- // FIXME: Might be less than 3 bytes
- if (!is->hasDataOrRestore(3))
- return false;
-
- len = readCompact(is);
- os->writeOpaque32(len);
-
- if (!is->hasDataOrRestore(len))
- return false;
-
- os->copyBytes(is, len);
-
- is->clearRestorePoint();
-
- return true;
- }
-
- // Quit on unsupported compression type.
- if (comp_ctl > tightMaxSubencoding)
- throw Exception("TightDecoder: bad subencoding value received");
-
- // "Basic" compression type.
-
- int palSize = 0;
-
- if (r.width() > TIGHT_MAX_WIDTH)
- throw Exception("TightDecoder: too large rectangle (%d pixels)", r.width());
-
- // Possible palette
- if ((comp_ctl & tightExplicitFilter) != 0) {
- uint8_t filterId;
-
- if (!is->hasDataOrRestore(1))
- return false;
-
- filterId = is->readU8();
- os->writeU8(filterId);
-
- switch (filterId) {
- case tightFilterPalette:
- if (!is->hasDataOrRestore(1))
- return false;
-
- palSize = is->readU8() + 1;
- os->writeU8(palSize - 1);
-
- if (server.pf().is888()) {
- if (!is->hasDataOrRestore(palSize * 3))
- return false;
- os->copyBytes(is, palSize * 3);
- } else {
- if (!is->hasDataOrRestore(palSize * server.pf().bpp/8))
- return false;
- os->copyBytes(is, palSize * server.pf().bpp/8);
- }
- break;
- case tightFilterGradient:
- if (server.pf().bpp == 8)
- throw Exception("TightDecoder: invalid BPP for gradient filter");
- break;
- case tightFilterCopy:
- break;
- default:
- throw Exception("TightDecoder: unknown filter code received");
- }
- }
-
- size_t rowSize, dataSize;
-
- if (palSize != 0) {
- if (palSize <= 2)
- rowSize = (r.width() + 7) / 8;
- else
- rowSize = r.width();
- } else if (server.pf().is888()) {
- rowSize = r.width() * 3;
- } else {
- rowSize = r.width() * server.pf().bpp/8;
- }
-
- dataSize = r.height() * rowSize;
-
- if (dataSize < TIGHT_MIN_TO_COMPRESS) {
- if (!is->hasDataOrRestore(dataSize))
- return false;
- os->copyBytes(is, dataSize);
- } else {
- uint32_t len;
-
- // FIXME: Might be less than 3 bytes
- if (!is->hasDataOrRestore(3))
- return false;
-
- len = readCompact(is);
- os->writeOpaque32(len);
-
- if (!is->hasDataOrRestore(len))
- return false;
-
- os->copyBytes(is, len);
- }
-
- is->clearRestorePoint();
-
- return true;
- }
-
- bool TightDecoder::doRectsConflict(const Rect& /*rectA*/,
- const uint8_t* bufferA,
- size_t buflenA,
- const Rect& /*rectB*/,
- const uint8_t* bufferB,
- size_t buflenB,
- const ServerParams& /*server*/)
- {
- uint8_t comp_ctl_a, comp_ctl_b;
-
- assert(buflenA >= 1);
- assert(buflenB >= 1);
-
- comp_ctl_a = *(const uint8_t*)bufferA;
- comp_ctl_b = *(const uint8_t*)bufferB;
-
- // Resets or use of zlib pose the same problem, so merge them
- if ((comp_ctl_a & 0x80) == 0x00)
- comp_ctl_a |= 1 << ((comp_ctl_a >> 4) & 0x03);
- if ((comp_ctl_b & 0x80) == 0x00)
- comp_ctl_b |= 1 << ((comp_ctl_b >> 4) & 0x03);
-
- if (((comp_ctl_a & 0x0f) & (comp_ctl_b & 0x0f)) != 0)
- return true;
-
- return false;
- }
-
- void TightDecoder::decodeRect(const Rect& r, const uint8_t* buffer,
- size_t buflen, const ServerParams& server,
- ModifiablePixelBuffer* pb)
- {
- const uint8_t* bufptr;
- const PixelFormat& pf = server.pf();
-
- uint8_t comp_ctl;
-
- bufptr = (const uint8_t*)buffer;
-
- assert(buflen >= 1);
-
- comp_ctl = *bufptr;
- bufptr += 1;
- buflen -= 1;
-
- // Reset zlib streams if we are told by the server to do so.
- for (int i = 0; i < 4; i++) {
- if (comp_ctl & 1) {
- zis[i].reset();
- }
- comp_ctl >>= 1;
- }
-
- // "Fill" compression type.
- if (comp_ctl == tightFill) {
- if (pf.is888()) {
- uint8_t pix[4];
-
- assert(buflen >= 3);
-
- pf.bufferFromRGB(pix, bufptr, 1);
- pb->fillRect(pf, r, pix);
- } else {
- assert(buflen >= (size_t)pf.bpp/8);
- pb->fillRect(pf, r, bufptr);
- }
- return;
- }
-
- // "JPEG" compression type.
- if (comp_ctl == tightJpeg) {
- uint32_t len;
-
- int stride;
- uint8_t *buf;
-
- JpegDecompressor jd;
-
- assert(buflen >= 4);
-
- memcpy(&len, bufptr, 4);
- bufptr += 4;
- buflen -= 4;
-
- // We always use direct decoding with JPEG images
- buf = pb->getBufferRW(r, &stride);
- jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
- pb->commitBufferRW(r);
- return;
- }
-
- // Quit on unsupported compression type.
- assert(comp_ctl <= tightMaxSubencoding);
-
- // "Basic" compression type.
-
- int palSize = 0;
- uint8_t palette[256 * 4];
- bool useGradient = false;
-
- if ((comp_ctl & tightExplicitFilter) != 0) {
- uint8_t filterId;
-
- assert(buflen >= 1);
-
- filterId = *bufptr;
- bufptr += 1;
- buflen -= 1;
-
- switch (filterId) {
- case tightFilterPalette:
- assert(buflen >= 1);
-
- palSize = *bufptr + 1;
- bufptr += 1;
- buflen -= 1;
-
- if (pf.is888()) {
- size_t len = palSize * 3;
- std::vector<uint8_t> tightPalette(len);
-
- assert(buflen >= len);
-
- memcpy(tightPalette.data(), bufptr, len);
- bufptr += len;
- buflen -= len;
-
- pf.bufferFromRGB(palette, tightPalette.data(), palSize);
- } else {
- size_t len;
-
- len = palSize * pf.bpp/8;
-
- assert(buflen >= len);
-
- memcpy(palette, bufptr, len);
- bufptr += len;
- buflen -= len;
- }
- break;
- case tightFilterGradient:
- useGradient = true;
- break;
- case tightFilterCopy:
- break;
- default:
- assert(false);
- }
- }
-
- // Determine if the data should be decompressed or just copied.
- size_t rowSize, dataSize;
- uint8_t* netbuf;
-
- netbuf = NULL;
-
- if (palSize != 0) {
- if (palSize <= 2)
- rowSize = (r.width() + 7) / 8;
- else
- rowSize = r.width();
- } else if (pf.is888()) {
- rowSize = r.width() * 3;
- } else {
- rowSize = r.width() * pf.bpp/8;
- }
-
- dataSize = r.height() * rowSize;
-
- if (dataSize < TIGHT_MIN_TO_COMPRESS)
- assert(buflen >= dataSize);
- else {
- uint32_t len;
- int streamId;
- rdr::MemInStream* ms;
-
- assert(buflen >= 4);
-
- memcpy(&len, bufptr, 4);
- bufptr += 4;
- buflen -= 4;
-
- assert(buflen >= len);
-
- streamId = comp_ctl & 0x03;
- ms = new rdr::MemInStream(bufptr, len);
- zis[streamId].setUnderlying(ms, len);
-
- // Allocate buffer and decompress the data
- netbuf = new uint8_t[dataSize];
-
- if (!zis[streamId].hasData(dataSize))
- throw Exception("Tight decode error");
- zis[streamId].readBytes(netbuf, dataSize);
-
- zis[streamId].flushUnderlying();
- zis[streamId].setUnderlying(NULL, 0);
- delete ms;
-
- bufptr = netbuf;
- buflen = dataSize;
- }
-
- // Time to decode the actual data
- bool directDecode;
-
- uint8_t* outbuf;
- int stride;
-
- if (pb->getPF() == pf) {
- // Decode directly into the framebuffer (fast path)
- directDecode = true;
- } else {
- // Decode into an intermediate buffer and use pixel translation
- directDecode = false;
- }
-
- if (directDecode)
- outbuf = pb->getBufferRW(r, &stride);
- else {
- outbuf = new uint8_t[r.area() * (pf.bpp/8)];
- stride = r.width();
- }
-
- if (palSize == 0) {
- // Truecolor data
- if (useGradient) {
- if (pf.is888())
- FilterGradient24(bufptr, pf, (uint32_t*)outbuf, stride, r);
- else {
- switch (pf.bpp) {
- case 8:
- assert(false);
- break;
- case 16:
- FilterGradient(bufptr, pf, (uint16_t*)outbuf, stride, r);
- break;
- case 32:
- FilterGradient(bufptr, pf, (uint32_t*)outbuf, stride, r);
- break;
- }
- }
- } else {
- // Copy
- uint8_t* ptr = outbuf;
- const uint8_t* srcPtr = bufptr;
- int w = r.width();
- int h = r.height();
- if (pf.is888()) {
- while (h > 0) {
- pf.bufferFromRGB(ptr, srcPtr, w);
- ptr += stride * pf.bpp/8;
- srcPtr += w * 3;
- h--;
- }
- } else {
- while (h > 0) {
- memcpy(ptr, srcPtr, w * pf.bpp/8);
- ptr += stride * pf.bpp/8;
- srcPtr += w * pf.bpp/8;
- h--;
- }
- }
- }
- } else {
- // Indexed color
- switch (pf.bpp) {
- case 8:
- FilterPalette((const uint8_t*)palette, palSize,
- bufptr, (uint8_t*)outbuf, stride, r);
- break;
- case 16:
- FilterPalette((const uint16_t*)palette, palSize,
- bufptr, (uint16_t*)outbuf, stride, r);
- break;
- case 32:
- FilterPalette((const uint32_t*)palette, palSize,
- bufptr, (uint32_t*)outbuf, stride, r);
- break;
- }
- }
-
- if (directDecode)
- pb->commitBufferRW(r);
- else {
- pb->imageRect(pf, r, outbuf);
- delete [] outbuf;
- }
-
- delete [] netbuf;
- }
-
- uint32_t TightDecoder::readCompact(rdr::InStream* is)
- {
- uint8_t b;
- uint32_t result;
-
- b = is->readU8();
- result = (int)b & 0x7F;
- if (b & 0x80) {
- b = is->readU8();
- result |= ((int)b & 0x7F) << 7;
- if (b & 0x80) {
- b = is->readU8();
- result |= ((int)b & 0xFF) << 14;
- }
- }
-
- return result;
- }
-
- void
- TightDecoder::FilterGradient24(const uint8_t *inbuf,
- const PixelFormat& pf, uint32_t* outbuf,
- int stride, const Rect& r)
- {
- int x, y, c;
- uint8_t prevRow[TIGHT_MAX_WIDTH*3];
- uint8_t thisRow[TIGHT_MAX_WIDTH*3];
- uint8_t pix[3];
- int est[3];
-
- memset(prevRow, 0, sizeof(prevRow));
-
- // Set up shortcut variables
- int rectHeight = r.height();
- int rectWidth = r.width();
-
- for (y = 0; y < rectHeight; y++) {
- for (x = 0; x < rectWidth; x++) {
- /* First pixel in a row */
- if (x == 0) {
- for (c = 0; c < 3; c++) {
- pix[c] = inbuf[y*rectWidth*3+c] + prevRow[c];
- thisRow[c] = pix[c];
- }
- pf.bufferFromRGB((uint8_t*)&outbuf[y*stride], pix, 1);
- continue;
- }
-
- for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 0xff) {
- est[c] = 0xff;
- } else if (est[c] < 0) {
- est[c] = 0;
- }
- pix[c] = inbuf[(y*rectWidth+x)*3+c] + est[c];
- thisRow[x*3+c] = pix[c];
- }
- pf.bufferFromRGB((uint8_t*)&outbuf[y*stride+x], pix, 1);
- }
-
- memcpy(prevRow, thisRow, sizeof(prevRow));
- }
- }
-
- template<class T>
- void TightDecoder::FilterGradient(const uint8_t* inbuf,
- const PixelFormat& pf, T* outbuf,
- int stride, const Rect& r)
- {
- int x, y, c;
- static uint8_t prevRow[TIGHT_MAX_WIDTH*3];
- static uint8_t thisRow[TIGHT_MAX_WIDTH*3];
- uint8_t pix[3];
- int est[3];
-
- memset(prevRow, 0, sizeof(prevRow));
-
- // Set up shortcut variables
- int rectHeight = r.height();
- int rectWidth = r.width();
-
- for (y = 0; y < rectHeight; y++) {
- for (x = 0; x < rectWidth; x++) {
- /* First pixel in a row */
- if (x == 0) {
- pf.rgbFromBuffer(pix, &inbuf[y*rectWidth], 1);
- for (c = 0; c < 3; c++)
- pix[c] += prevRow[c];
-
- memcpy(thisRow, pix, sizeof(pix));
-
- pf.bufferFromRGB((uint8_t*)&outbuf[y*stride], pix, 1);
-
- continue;
- }
-
- for (c = 0; c < 3; c++) {
- est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
- if (est[c] > 255) {
- est[c] = 255;
- } else if (est[c] < 0) {
- est[c] = 0;
- }
- }
-
- pf.rgbFromBuffer(pix, &inbuf[y*rectWidth+x], 1);
- for (c = 0; c < 3; c++)
- pix[c] += est[c];
-
- memcpy(&thisRow[x*3], pix, sizeof(pix));
-
- pf.bufferFromRGB((uint8_t*)&outbuf[y*stride+x], pix, 1);
- }
-
- memcpy(prevRow, thisRow, sizeof(prevRow));
- }
- }
-
- template<class T>
- void TightDecoder::FilterPalette(const T* palette, int palSize,
- const uint8_t* inbuf, T* outbuf,
- int stride, const Rect& r)
- {
- // Indexed color
- int x, h = r.height(), w = r.width(), b, pad = stride - w;
- T* ptr = outbuf;
- uint8_t bits;
- const uint8_t* srcPtr = inbuf;
- if (palSize <= 2) {
- // 2-color palette
- while (h > 0) {
- for (x = 0; x < w / 8; x++) {
- bits = *srcPtr++;
- for (b = 7; b >= 0; b--) {
- *ptr++ = palette[bits >> b & 1];
- }
- }
- if (w % 8 != 0) {
- bits = *srcPtr++;
- for (b = 7; b >= 8 - w % 8; b--) {
- *ptr++ = palette[bits >> b & 1];
- }
- }
- ptr += pad;
- h--;
- }
- } else {
- // 256-color palette
- while (h > 0) {
- T *endOfRow = ptr + w;
- while (ptr < endOfRow) {
- *ptr++ = palette[*srcPtr++];
- }
- ptr += pad;
- h--;
- }
- }
- }
|