123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
- * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB.
- *
- * 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.
- */
-
- //
- // tightEncode.h - Tight encoding function.
- //
- // This file is #included after having set the following macro:
- // BPP - 8, 16 or 32
- //
-
- #include <assert.h>
-
- namespace rfb {
-
- // CONCAT2E concatenates its arguments, expanding them if they are macros
-
- #ifndef CONCAT2E
- #define CONCAT2(a,b) a##b
- #define CONCAT2E(a,b) CONCAT2(a,b)
- #endif
-
- #define PIXEL_T rdr::CONCAT2E(U,BPP)
- #define TIGHT_ENCODE TightEncoder::CONCAT2E(tightEncode,BPP)
- #define HASH_FUNCTION CONCAT2E(HASH_FUNC,BPP)
- #define PACK_PIXELS TightEncoder::CONCAT2E(packPixels,BPP)
- #define ENCODE_SOLID_RECT TightEncoder::CONCAT2E(encodeSolidRect,BPP)
- #define ENCODE_FULLCOLOR_RECT TightEncoder::CONCAT2E(encodeFullColorRect,BPP)
- #define ENCODE_MONO_RECT TightEncoder::CONCAT2E(encodeMonoRect,BPP)
- #define ENCODE_INDEXED_RECT TightEncoder::CONCAT2E(encodeIndexedRect,BPP)
- #define ENCODE_JPEG_RECT TightEncoder::CONCAT2E(encodeJpegRect,BPP)
- #define FAST_FILL_PALETTE TightEncoder::CONCAT2E(fastFillPalette,BPP)
- #define FILL_PALETTE TightEncoder::CONCAT2E(fillPalette,BPP)
- #define CHECK_SOLID_TILE TightEncoder::CONCAT2E(checkSolidTile,BPP)
-
- #ifndef TIGHT_ONCE
- #define TIGHT_ONCE
-
- //
- // Compress the data (but do not perform actual compression if the data
- // size is less than TIGHT_MIN_TO_COMPRESS bytes.
- //
-
- void TightEncoder::compressData(const void *buf, unsigned int length,
- rdr::ZlibOutStream *zos, int zlibLevel,
- rdr::OutStream *os)
- {
- if (length < TIGHT_MIN_TO_COMPRESS) {
- os->writeBytes(buf, length);
- } else {
- // FIXME: Using a temporary MemOutStream may be not efficient.
- // Maybe use the same static object used in the JPEG coder?
- int maxBeforeSize = pconf->maxRectSize * (clientpf.bpp / 8);
- int maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
- rdr::MemOutStream mem_os(maxAfterSize);
- zos->setUnderlying(&mem_os);
- zos->setCompressionLevel(zlibLevel);
- zos->writeBytes(buf, length);
- zos->flush();
- zos->setUnderlying(NULL);
- writeCompact(os, mem_os.length());
- os->writeBytes(mem_os.data(), mem_os.length());
- }
- }
-
- #endif // #ifndef TIGHT_ONCE
-
- //
- // Convert 32-bit color samples into 24-bit colors, in place.
- // Performs packing only when redMax, greenMax and blueMax are all 255.
- // Color components are assumed to be byte-aligned.
- //
-
- unsigned int PACK_PIXELS (PIXEL_T *buf, unsigned int count)
- {
- #if (BPP != 32)
- return count * sizeof(PIXEL_T);
- #else
- if (!pack24)
- return count * sizeof(PIXEL_T);
-
- rdr::U32 pix;
- rdr::U8 *dst = (rdr::U8 *)buf;
- for (unsigned int i = 0; i < count; i++) {
- pix = *buf++;
- clientpf.rgbFromBuffer(dst, (rdr::U8*)&pix, 1);
- dst += 3;
- }
- return count * 3;
- #endif
- }
-
- //
- // Main function of the Tight encoder
- //
-
- void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os, bool forceSolid)
- {
- int stride;
- rdr::U32 solidColor;
- const PIXEL_T *rawPixels = (const PIXEL_T *)pb->getBuffer(r, &stride);
- PIXEL_T *pixels = NULL;
- bool grayScaleJPEG = (jpegSubsampling == subsampleGray && jpegQuality != -1);
-
- #if (BPP == 32)
- // Check if it's necessary to pack 24-bit pixels, and
- // compute appropriate shift values if necessary.
- pack24 = clientpf.is888();
- #endif
-
- if (forceSolid) {
- // Subrectangle has already been determined to be solid.
- clientpf.bufferFromBuffer((rdr::U8*)&solidColor, serverpf,
- (const rdr::U8*)rawPixels, 1);
- pixels = (PIXEL_T *)&solidColor;
- palette.clear();
- palette.insert(solidColor, 1);
- } else {
- // Analyze subrectangle's colors to determine best encoding method.
- palMaxColors = r.area() / pconf->idxMaxColorsDivisor;
- if (jpegQuality != -1)
- palMaxColors = pconf->palMaxColorsWithJPEG;
- if (palMaxColors < 2 && r.area() >= pconf->monoMinRectSize)
- palMaxColors = 2;
-
- if (clientpf.equal(serverpf) && clientpf.bpp >= 16) {
- // Count the colors in the raw buffer, so we can avoid unnecessary pixel
- // translation when encoding with JPEG.
- if (grayScaleJPEG) palette.clear();
- else FAST_FILL_PALETTE(rawPixels, stride, r);
-
- // JPEG can read from the raw buffer, but for the other methods, we need
- // to translate the raw pixels into an intermediate buffer.
- if(palette.size() != 0 || jpegQuality == -1) {
- pixels = (PIXEL_T *)conn->writer()->getImageBuf(r.area());
- stride = r.width();
- pb->getImage(clientpf, pixels, r);
- }
- } else {
- // Pixel translation will be required, so create an intermediate buffer,
- // translate the raw pixels into it, and count its colors.
- pixels = (PIXEL_T *)conn->writer()->getImageBuf(r.area());
- stride = r.width();
- pb->getImage(clientpf, pixels, r);
-
- if (grayScaleJPEG) palette.clear();
- else FILL_PALETTE(pixels, r.area());
- }
- }
-
- switch (palette.size()) {
- case 0:
- // Truecolor image
- #if (BPP != 8)
- if (jpegQuality != -1) {
- if (pixels)
- ENCODE_JPEG_RECT(pixels, stride, r, os);
- else
- ENCODE_JPEG_RECT((PIXEL_T *)rawPixels, stride, r, os);
- break;
- }
- #endif
- ENCODE_FULLCOLOR_RECT(pixels, r, os);
- break;
- case 1:
- // Solid rectangle
- ENCODE_SOLID_RECT(pixels, os);
- break;
- case 2:
- // Two-color rectangle
- ENCODE_MONO_RECT(pixels, r, os);
- break;
- #if (BPP != 8)
- default:
- // Up to 256 different colors
- ENCODE_INDEXED_RECT(pixels, r, os);
- #endif
- }
- }
-
- //
- // Subencoding implementations.
- //
-
- void ENCODE_SOLID_RECT (PIXEL_T *buf, rdr::OutStream *os)
- {
- os->writeU8(0x08 << 4);
-
- int length = PACK_PIXELS(buf, 1);
- os->writeBytes(buf, length);
- }
-
- void ENCODE_FULLCOLOR_RECT (PIXEL_T *buf, const Rect& r, rdr::OutStream *os)
- {
- const int streamId = 0;
- os->writeU8(streamId << 4);
-
- int length = PACK_PIXELS(buf, r.area());
- compressData(buf, length, &zos[streamId], pconf->rawZlibLevel, os);
- }
-
- void ENCODE_MONO_RECT (PIXEL_T *buf, const Rect& r, rdr::OutStream *os)
- {
- const int streamId = 1;
- os->writeU8((streamId | 0x04) << 4);
- os->writeU8(0x01);
-
- // Write the palette
- PIXEL_T pal[2] = { (PIXEL_T)palette.getColour(0),
- (PIXEL_T)palette.getColour(1) };
- os->writeU8(1);
- os->writeBytes(pal, PACK_PIXELS(pal, 2));
-
- // Encode the data in-place
- PIXEL_T *src = buf;
- rdr::U8 *dst = (rdr::U8 *)buf;
- int w = r.width();
- int h = r.height();
- PIXEL_T bg;
- unsigned int value, mask;
- int aligned_width;
- int x, y, bg_bits;
-
- bg = (PIXEL_T) pal[0];
- aligned_width = w - w % 8;
-
- for (y = 0; y < h; y++) {
- for (x = 0; x < aligned_width; x += 8) {
- for (bg_bits = 0; bg_bits < 8; bg_bits++) {
- if (*src++ != bg)
- break;
- }
- if (bg_bits == 8) {
- *dst++ = 0;
- continue;
- }
- mask = 0x80 >> bg_bits;
- value = mask;
- for (bg_bits++; bg_bits < 8; bg_bits++) {
- mask >>= 1;
- if (*src++ != bg) {
- value |= mask;
- }
- }
- *dst++ = (rdr::U8)value;
- }
-
- mask = 0x80;
- value = 0;
- if (x >= w)
- continue;
-
- for (; x < w; x++) {
- if (*src++ != bg) {
- value |= mask;
- }
- mask >>= 1;
- }
- *dst++ = (rdr::U8)value;
- }
-
- // Write the data
- int length = (w + 7) / 8;
- length *= h;
- compressData(buf, length, &zos[streamId], pconf->monoZlibLevel, os);
- }
-
- #if (BPP != 8)
- void ENCODE_INDEXED_RECT (PIXEL_T *buf, const Rect& r, rdr::OutStream *os)
- {
- const int streamId = 2;
- os->writeU8((streamId | 0x04) << 4);
- os->writeU8(0x01);
-
- // Write the palette
- {
- PIXEL_T pal[256];
- for (int i = 0; i < palette.size(); i++)
- pal[i] = (PIXEL_T)palette.getColour(i);
- os->writeU8((rdr::U8)(palette.size() - 1));
- os->writeBytes(pal, PACK_PIXELS(pal, palette.size()));
- }
-
- // Encode data in-place
- PIXEL_T *src = buf;
- rdr::U8 *dst = (rdr::U8 *)buf;
- int count = r.area();
- PIXEL_T rgb;
- int rep = 0;
- unsigned char idx;
-
- while (count--) {
- rgb = *src++;
- while (count && *src == rgb) {
- rep++, src++, count--;
- }
- idx = palette.lookup(rgb);
- *dst++ = idx;
- while (rep) {
- *dst++ = idx;
- rep--;
- }
- }
-
- // Write the data
- compressData(buf, r.area(), &zos[streamId], pconf->idxZlibLevel, os);
- }
- #endif // #if (BPP != 8)
-
- //
- // JPEG compression.
- //
-
- #if (BPP != 8)
- void ENCODE_JPEG_RECT (PIXEL_T *buf, int stride, const Rect& r,
- rdr::OutStream *os)
- {
- jc.clear();
- jc.compress((rdr::U8 *)buf, stride, r, clientpf,
- jpegQuality, jpegSubsampling);
- os->writeU8(0x09 << 4);
- writeCompact(os, jc.length());
- os->writeBytes(jc.data(), jc.length());
- }
- #endif // #if (BPP != 8)
-
- //
- // Determine the number of colors in the rectangle, and fill in the palette.
- //
-
- #if (BPP == 8)
-
- void FILL_PALETTE (PIXEL_T *data, int count)
- {
- PIXEL_T c0, c1;
- int i, n0, n1;
-
- palette.clear();
-
- c0 = data[0];
- for (i = 1; i < count && data[i] == c0; i++);
- if (i == count) {
- palette.insert(c0, i);
- return; // Solid rectangle
- }
-
- if (palMaxColors < 2)
- return;
-
- n0 = i;
- c1 = data[i];
- n1 = 0;
- for (i++; i < count; i++) {
- if (data[i] == c0) {
- n0++;
- } else if (data[i] == c1) {
- n1++;
- } else
- break;
- }
- if (i == count) {
- palette.insert(c0, n0); // Two colors
- palette.insert(c1, n1);
- }
- }
-
- void FAST_FILL_PALETTE (const PIXEL_T *data, int stride, const Rect& r)
- {
- }
-
- #else // (BPP != 8)
-
- void FILL_PALETTE (PIXEL_T *data, int count)
- {
- PIXEL_T c0, c1, ci = 0;
- int i, n0, n1, ni;
-
- palette.clear();
-
- c0 = data[0];
- for (i = 1; i < count && data[i] == c0; i++);
- if (i >= count) {
- palette.insert(c0, i); // Solid rectangle
- return;
- }
-
- if (palMaxColors < 2)
- return; // Full-color format preferred
-
- n0 = i;
- c1 = data[i];
- n1 = 0;
- for (i++; i < count; i++) {
- ci = data[i];
- if (ci == c0) {
- n0++;
- } else if (ci == c1) {
- n1++;
- } else
- break;
- }
- palette.insert(c0, n0);
- palette.insert(c1, n1);
- if (i >= count)
- return; // Two colors
-
- ni = 1;
- for (i++; i < count; i++) {
- if (data[i] == ci) {
- ni++;
- } else {
- if (!palette.insert (ci, ni) || (palette.size() > palMaxColors)) {
- palette.clear();
- return;
- }
- ci = data[i];
- ni = 1;
- }
- }
- if (!palette.insert (ci, ni) || (palette.size() > palMaxColors))
- palette.clear();
- }
-
- void FAST_FILL_PALETTE (const PIXEL_T *data, int stride, const Rect& r)
- {
- PIXEL_T c0, c1, ci = 0, mask, c0t, c1t, cit;
- int n0, n1, ni;
- int w = r.width(), h = r.height();
- const PIXEL_T *rowptr, *colptr, *rowptr2, *colptr2,
- *dataend = &data[stride * h];
- bool willTransform = !serverpf.equal(clientpf);
-
- serverpf.bufferFromPixel((rdr::U8*)&mask, ~0);
-
- palette.clear();
-
- c0 = data[0] & mask;
- n0 = 0;
- for (rowptr = data; rowptr < dataend; rowptr += stride) {
- for (colptr = rowptr; colptr < &rowptr[w]; colptr++) {
- if (((*colptr) & mask) != c0)
- goto soliddone;
- n0++;
- }
- }
-
- soliddone:
- if (rowptr >= dataend) {
- palette.insert(c0, 1); // Solid rectangle
- return;
- }
- if (palMaxColors < 2)
- return; // Full-color format preferred
-
- c1 = *colptr & mask;
- n1 = 0;
- colptr++;
- if (colptr >= &rowptr[w]) {
- rowptr += stride; colptr = rowptr;
- }
- colptr2 = colptr;
- for (rowptr2 = rowptr; rowptr2 < dataend;) {
- for (; colptr2 < &rowptr2[w]; colptr2++) {
- ci = (*colptr2) & mask;
- if (ci == c0) {
- n0++;
- } else if (ci == c1) {
- n1++;
- } else
- goto monodone;
- }
- rowptr2 += stride;
- colptr2 = rowptr2;
- }
-
- monodone:
- if (willTransform) {
- clientpf.bufferFromBuffer((rdr::U8*)&c0t, serverpf, (rdr::U8*)&c0, 1);
- clientpf.bufferFromBuffer((rdr::U8*)&c1t, serverpf, (rdr::U8*)&c1, 1);
- }
- else {
- c0t = c0; c1t = c1;
- }
-
- palette.insert(c0t, n0);
- palette.insert(c1t, n1);
-
- if (colptr2 >= dataend)
- return; // Two colors
-
- ni = 1;
- colptr2++;
- if (colptr2 >= &rowptr2[w]) {
- rowptr2 += stride; colptr2 = rowptr2;
- }
- colptr = colptr2;
- for (rowptr = rowptr2; rowptr < dataend;) {
- for (; colptr < &rowptr[w]; colptr++) {
- if (((*colptr) & mask) == ci) {
- ni++;
- } else {
- if (willTransform)
- clientpf.bufferFromBuffer((rdr::U8*)&cit, serverpf, (rdr::U8*)&ci, 1);
- else
- cit = ci;
- if (!palette.insert (cit, ni) || (palette.size() > palMaxColors)) {
- palette.clear();
- return;
- }
- ci = (*colptr) & mask;
- ni = 1;
- }
- }
- rowptr += stride;
- colptr = rowptr;
- }
- clientpf.bufferFromBuffer((rdr::U8*)&cit, serverpf, (rdr::U8*)&ci, 1);
- if (!palette.insert (cit, ni) || (palette.size() > palMaxColors))
- palette.clear();
- }
-
- #endif // #if (BPP == 8)
-
- bool CHECK_SOLID_TILE(Rect& r, rdr::U32 *colorPtr, bool needSameColor)
- {
- const PIXEL_T *buf;
- PIXEL_T colorValue;
- int w = r.width(), h = r.height();
-
- int stride = w;
- buf = (const PIXEL_T *)pb->getBuffer(r, &stride);
-
- colorValue = *buf;
- if (needSameColor && (rdr::U32)colorValue != *colorPtr)
- return false;
-
- int bufPad = stride - w;
- while (h > 0) {
- const PIXEL_T *bufEndOfRow = buf + w;
- while (buf < bufEndOfRow) {
- if (colorValue != *(buf++))
- return false;
- }
- buf += bufPad;
- h--;
- }
-
- *colorPtr = (rdr::U32)colorValue;
- return true;
- }
-
- #undef PIXEL_T
- #undef TIGHT_ENCODE
- #undef HASH_FUNCTION
- #undef PACK_PIXELS
- #undef ENCODE_SOLID_RECT
- #undef ENCODE_FULLCOLOR_RECT
- #undef ENCODE_MONO_RECT
- #undef ENCODE_INDEXED_RECT
- #undef ENCODE_JPEG_RECT
- #undef FAST_FILL_PALETTE
- #undef FILL_PALETTE
- #undef CHECK_SOLID_TILE
- }
|