123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2009-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.
- */
- #include <assert.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <string.h>
- #include <rdr/InStream.h>
- #include <rdr/OutStream.h>
- #include <rfb/Exception.h>
- #include <rfb/PixelFormat.h>
- #include <rfb/util.h>
-
- #ifdef _WIN32
- #define strcasecmp _stricmp
- #endif
-
- using namespace rfb;
-
- rdr::U8 PixelFormat::upconvTable[256*8];
- rdr::U8 PixelFormat::downconvTable[256*8];
-
- class PixelFormat::Init {
- public:
- Init();
- };
-
- PixelFormat::Init PixelFormat::_init;
-
-
- PixelFormat::Init::Init()
- {
- int bits;
-
- // Shifting bits is almost perfect, but not quite. And
- // a lookup table is still quicker when there is a large
- // difference between the source and destination depth.
-
- for (bits = 1;bits <= 8;bits++) {
- int i, maxVal;
- rdr::U8 *subUpTable;
- rdr::U8 *subDownTable;
-
- maxVal = (1 << bits) - 1;
- subUpTable = &upconvTable[(bits-1)*256];
- subDownTable = &downconvTable[(bits-1)*256];
-
- for (i = 0;i <= maxVal;i++)
- subUpTable[i] = i * 255 / maxVal;
-
- // Duplicate the up table so that we don't have to care about
- // the upper bits when doing a lookup
- for (;i < 256;i += maxVal+1)
- memcpy(&subUpTable[i], &subUpTable[0], maxVal+1);
-
- for (i = 0;i <= 255;i++)
- subDownTable[i] = (i * maxVal + 128) / 255;
- }
- }
-
-
- PixelFormat::PixelFormat(int b, int d, bool e, bool t,
- int rm, int gm, int bm, int rs, int gs, int bs)
- : bpp(b), depth(d), trueColour(t), bigEndian(e),
- redMax(rm), greenMax(gm), blueMax(bm),
- redShift(rs), greenShift(gs), blueShift(bs)
- {
- if (!isSane())
- throw Exception("invalid pixel format");
-
- updateState();
- }
-
- PixelFormat::PixelFormat()
- : bpp(8), depth(8), trueColour(true), bigEndian(false),
- redMax(7), greenMax(7), blueMax(3),
- redShift(0), greenShift(3), blueShift(6)
- {
- updateState();
- }
-
- bool PixelFormat::equal(const PixelFormat& other) const
- {
- if (bpp != other.bpp || depth != other.depth)
- return false;
-
- if (redMax != other.redMax)
- return false;
- if (greenMax != other.greenMax)
- return false;
- if (blueMax != other.blueMax)
- return false;
-
- // Endianness requires more care to determine compatibility
- if (bigEndian == other.bigEndian || bpp == 8) {
- if (redShift != other.redShift)
- return false;
- if (greenShift != other.greenShift)
- return false;
- if (blueShift != other.blueShift)
- return false;
- } else {
- // Has to be the same byte for each channel
- if (redShift/8 != (3 - other.redShift/8))
- return false;
- if (greenShift/8 != (3 - other.greenShift/8))
- return false;
- if (blueShift/8 != (3 - other.blueShift/8))
- return false;
-
- // And the same bit offset within the byte
- if (redShift%8 != other.redShift%8)
- return false;
- if (greenShift%8 != other.greenShift%8)
- return false;
- if (blueShift%8 != other.blueShift%8)
- return false;
-
- // And not cross a byte boundary
- if (redShift/8 != (redShift + redBits - 1)/8)
- return false;
- if (greenShift/8 != (greenShift + greenBits - 1)/8)
- return false;
- if (blueShift/8 != (blueShift + blueBits - 1)/8)
- return false;
- }
-
- return true;
- }
-
- void PixelFormat::read(rdr::InStream* is)
- {
- bpp = is->readU8();
- depth = is->readU8();
- bigEndian = is->readU8();
- trueColour = is->readU8();
- redMax = is->readU16();
- greenMax = is->readU16();
- blueMax = is->readU16();
- redShift = is->readU8();
- greenShift = is->readU8();
- blueShift = is->readU8();
- is->skip(3);
-
- // We have no real support for colour maps. If the client
- // wants one, then we force a 8-bit true colour format and
- // pretend it's a colour map.
- if (!trueColour) {
- redMax = 7;
- greenMax = 7;
- blueMax = 3;
- redShift = 0;
- greenShift = 3;
- blueShift = 6;
- }
-
- if (!isSane())
- throw Exception("invalid pixel format");
-
- updateState();
- }
-
- void PixelFormat::write(rdr::OutStream* os) const
- {
- os->writeU8(bpp);
- os->writeU8(depth);
- os->writeU8(bigEndian);
- os->writeU8(trueColour);
- os->writeU16(redMax);
- os->writeU16(greenMax);
- os->writeU16(blueMax);
- os->writeU8(redShift);
- os->writeU8(greenShift);
- os->writeU8(blueShift);
- os->pad(3);
- }
-
-
- bool PixelFormat::is888(void) const
- {
- if (!trueColour)
- return false;
- if (bpp != 32)
- return false;
- if (depth != 24)
- return false;
- if (redMax != 255)
- return false;
- if (greenMax != 255)
- return false;
- if (blueMax != 255)
- return false;
- if ((redShift & 0x7) != 0)
- return false;
- if ((greenShift & 0x7) != 0)
- return false;
- if ((blueShift & 0x7) != 0)
- return false;
-
- return true;
- }
-
-
- bool PixelFormat::isBigEndian(void) const
- {
- return bigEndian;
- }
-
-
- bool PixelFormat::isLittleEndian(void) const
- {
- return ! bigEndian;
- }
-
-
- void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src, int pixels) const
- {
- bufferFromRGB(dst, src, pixels, pixels, 1);
- }
-
- void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
- int w, int stride, int h) const
- {
- if (is888()) {
- // Optimised common case
- rdr::U8 *r, *g, *b, *x;
-
- if (bigEndian) {
- r = dst + (24 - redShift)/8;
- g = dst + (24 - greenShift)/8;
- b = dst + (24 - blueShift)/8;
- x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
- } else {
- r = dst + redShift/8;
- g = dst + greenShift/8;
- b = dst + blueShift/8;
- x = dst + (48 - redShift - greenShift - blueShift)/8;
- }
-
- int dstPad = (stride - w) * 4;
- while (h--) {
- int w_ = w;
- while (w_--) {
- *r = *(src++);
- *g = *(src++);
- *b = *(src++);
- *x = 0;
- r += 4;
- g += 4;
- b += 4;
- x += 4;
- }
- r += dstPad;
- g += dstPad;
- b += dstPad;
- x += dstPad;
- }
- } else {
- // Generic code
- int dstPad = (stride - w) * bpp/8;
- while (h--) {
- int w_ = w;
- while (w_--) {
- Pixel p;
- rdr::U8 r, g, b;
-
- r = *(src++);
- g = *(src++);
- b = *(src++);
-
- p = pixelFromRGB(r, g, b);
-
- bufferFromPixel(dst, p);
- dst += bpp/8;
- }
- dst += dstPad;
- }
- }
- }
-
-
- void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels) const
- {
- rgbFromBuffer(dst, src, pixels, pixels, 1);
- }
-
-
- void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
- int w, int stride, int h) const
- {
- if (is888()) {
- // Optimised common case
- const rdr::U8 *r, *g, *b;
-
- if (bigEndian) {
- r = src + (24 - redShift)/8;
- g = src + (24 - greenShift)/8;
- b = src + (24 - blueShift)/8;
- } else {
- r = src + redShift/8;
- g = src + greenShift/8;
- b = src + blueShift/8;
- }
-
- int srcPad = (stride - w) * 4;
- while (h--) {
- int w_ = w;
- while (w_--) {
- *(dst++) = *r;
- *(dst++) = *g;
- *(dst++) = *b;
- r += 4;
- g += 4;
- b += 4;
- }
- r += srcPad;
- g += srcPad;
- b += srcPad;
- }
- } else {
- // Generic code
- int srcPad = (stride - w) * bpp/8;
- while (h--) {
- int w_ = w;
- while (w_--) {
- Pixel p;
- rdr::U8 r, g, b;
-
- p = pixelFromBuffer(src);
-
- rgbFromPixel(p, &r, &g, &b);
-
- *(dst++) = r;
- *(dst++) = g;
- *(dst++) = b;
- src += bpp/8;
- }
- src += srcPad;
- }
- }
- }
-
-
- Pixel PixelFormat::pixelFromPixel(const PixelFormat &srcPF, Pixel src) const
- {
- rdr::U16 r, g, b;
- srcPF.rgbFromPixel(src, &r, &g, &b);
- return pixelFromRGB(r, g, b);
- }
-
-
- void PixelFormat::bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
- const rdr::U8* src, int pixels) const
- {
- bufferFromBuffer(dst, srcPF, src, pixels, 1, pixels, pixels);
- }
-
- #define IS_ALIGNED(v, a) (((intptr_t)v & (a-1)) == 0)
-
- void PixelFormat::bufferFromBuffer(rdr::U8* dst, const PixelFormat &srcPF,
- const rdr::U8* src, int w, int h,
- int dstStride, int srcStride) const
- {
- if (equal(srcPF)) {
- // Trivial case
- while (h--) {
- memcpy(dst, src, w * bpp/8);
- dst += dstStride * bpp/8;
- src += srcStride * srcPF.bpp/8;
- }
- } else if (is888() && srcPF.is888()) {
- // Optimised common case A: byte shuffling (e.g. endian conversion)
- rdr::U8 *d[4], *s[4];
- int dstPad, srcPad;
-
- if (bigEndian) {
- s[0] = dst + (24 - redShift)/8;
- s[1] = dst + (24 - greenShift)/8;
- s[2] = dst + (24 - blueShift)/8;
- s[3] = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
- } else {
- s[0] = dst + redShift/8;
- s[1] = dst + greenShift/8;
- s[2] = dst + blueShift/8;
- s[3] = dst + (48 - redShift - greenShift - blueShift)/8;
- }
-
- if (srcPF.bigEndian) {
- d[(24 - srcPF.redShift)/8] = s[0];
- d[(24 - srcPF.greenShift)/8] = s[1];
- d[(24 - srcPF.blueShift)/8] = s[2];
- d[(24 - (48 - srcPF.redShift - srcPF.greenShift - srcPF.blueShift))/8] = s[3];
- } else {
- d[srcPF.redShift/8] = s[0];
- d[srcPF.greenShift/8] = s[1];
- d[srcPF.blueShift/8] = s[2];
- d[(48 - srcPF.redShift - srcPF.greenShift - srcPF.blueShift)/8] = s[3];
- }
-
- dstPad = (dstStride - w) * 4;
- srcPad = (srcStride - w) * 4;
- while (h--) {
- int w_ = w;
- while (w_--) {
- *d[0] = *(src++);
- *d[1] = *(src++);
- *d[2] = *(src++);
- *d[3] = *(src++);
- d[0] += 4;
- d[1] += 4;
- d[2] += 4;
- d[3] += 4;
- }
- d[0] += dstPad;
- d[1] += dstPad;
- d[2] += dstPad;
- d[3] += dstPad;
- src += srcPad;
- }
- } else if (IS_ALIGNED(dst, bpp/8) && srcPF.is888()) {
- // Optimised common case B: 888 source
- switch (bpp) {
- case 8:
- directBufferFromBufferFrom888((rdr::U8*)dst, srcPF, src,
- w, h, dstStride, srcStride);
- break;
- case 16:
- directBufferFromBufferFrom888((rdr::U16*)dst, srcPF, src,
- w, h, dstStride, srcStride);
- break;
- case 32:
- directBufferFromBufferFrom888((rdr::U32*)dst, srcPF, src,
- w, h, dstStride, srcStride);
- break;
- }
- } else if (IS_ALIGNED(src, srcPF.bpp/8) && is888()) {
- // Optimised common case C: 888 destination
- switch (srcPF.bpp) {
- case 8:
- directBufferFromBufferTo888(dst, srcPF, (rdr::U8*)src,
- w, h, dstStride, srcStride);
- break;
- case 16:
- directBufferFromBufferTo888(dst, srcPF, (rdr::U16*)src,
- w, h, dstStride, srcStride);
- break;
- case 32:
- directBufferFromBufferTo888(dst, srcPF, (rdr::U32*)src,
- w, h, dstStride, srcStride);
- break;
- }
- } else {
- // Generic code
- int dstPad = (dstStride - w) * bpp/8;
- int srcPad = (srcStride - w) * srcPF.bpp/8;
- while (h--) {
- int w_ = w;
- while (w_--) {
- Pixel p;
- rdr::U8 r, g, b;
-
- p = srcPF.pixelFromBuffer(src);
- srcPF.rgbFromPixel(p, &r, &g, &b);
- p = pixelFromRGB(r, g, b);
- bufferFromPixel(dst, p);
-
- dst += bpp/8;
- src += srcPF.bpp/8;
- }
- dst += dstPad;
- src += srcPad;
- }
- }
- }
-
-
- void PixelFormat::print(char* str, int len) const
- {
- // Unfortunately snprintf is not widely available so we build the string up
- // using strncat - not pretty, but should be safe against buffer overruns.
-
- char num[20];
- if (len < 1) return;
- str[0] = 0;
- strncat(str, "depth ", len-1-strlen(str));
- sprintf(num,"%d",depth);
- strncat(str, num, len-1-strlen(str));
- strncat(str, " (", len-1-strlen(str));
- sprintf(num,"%d",bpp);
- strncat(str, num, len-1-strlen(str));
- strncat(str, "bpp)", len-1-strlen(str));
- if (bpp != 8) {
- if (bigEndian)
- strncat(str, " big-endian", len-1-strlen(str));
- else
- strncat(str, " little-endian", len-1-strlen(str));
- }
-
- if (!trueColour) {
- strncat(str, " color-map", len-1-strlen(str));
- return;
- }
-
- if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
- blueMax == (1 << greenShift) - 1 &&
- greenMax == (1 << (redShift-greenShift)) - 1 &&
- redMax == (1 << (depth-redShift)) - 1)
- {
- strncat(str, " rgb", len-1-strlen(str));
- sprintf(num,"%d",depth-redShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",redShift-greenShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",greenShift);
- strncat(str, num, len-1-strlen(str));
- return;
- }
-
- if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
- redMax == (1 << greenShift) - 1 &&
- greenMax == (1 << (blueShift-greenShift)) - 1 &&
- blueMax == (1 << (depth-blueShift)) - 1)
- {
- strncat(str, " bgr", len-1-strlen(str));
- sprintf(num,"%d",depth-blueShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",blueShift-greenShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",greenShift);
- strncat(str, num, len-1-strlen(str));
- return;
- }
-
- strncat(str, " rgb max ", len-1-strlen(str));
- sprintf(num,"%d,",redMax);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d,",greenMax);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",blueMax);
- strncat(str, num, len-1-strlen(str));
- strncat(str, " shift ", len-1-strlen(str));
- sprintf(num,"%d,",redShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d,",greenShift);
- strncat(str, num, len-1-strlen(str));
- sprintf(num,"%d",blueShift);
- strncat(str, num, len-1-strlen(str));
- }
-
-
- bool PixelFormat::parse(const char* str)
- {
- char rgbbgr[4];
- int bits1, bits2, bits3;
- if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
- return false;
-
- depth = bits1 + bits2 + bits3;
- bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
- trueColour = true;
- rdr::U32 endianTest = 1;
- bigEndian = (*(rdr::U8*)&endianTest == 0);
-
- greenShift = bits3;
- greenMax = (1 << bits2) - 1;
-
- if (strcasecmp(rgbbgr, "bgr") == 0) {
- redShift = 0;
- redMax = (1 << bits3) - 1;
- blueShift = bits3 + bits2;
- blueMax = (1 << bits1) - 1;
- } else if (strcasecmp(rgbbgr, "rgb") == 0) {
- blueShift = 0;
- blueMax = (1 << bits3) - 1;
- redShift = bits3 + bits2;
- redMax = (1 << bits1) - 1;
- } else {
- return false;
- }
-
- assert(isSane());
-
- updateState();
-
- return true;
- }
-
-
- static int bits(rdr::U16 value)
- {
- int bits;
-
- bits = 16;
-
- if (!(value & 0xff00)) {
- bits -= 8;
- value <<= 8;
- }
- if (!(value & 0xf000)) {
- bits -= 4;
- value <<= 4;
- }
- if (!(value & 0xc000)) {
- bits -= 2;
- value <<= 2;
- }
- if (!(value & 0x8000)) {
- bits -= 1;
- value <<= 1;
- }
-
- return bits;
- }
-
- void PixelFormat::updateState(void)
- {
- int endianTest = 1;
-
- redBits = bits(redMax);
- greenBits = bits(greenMax);
- blueBits = bits(blueMax);
-
- maxBits = redBits;
- if (greenBits > maxBits)
- maxBits = greenBits;
- if (blueBits > maxBits)
- maxBits = blueBits;
-
- minBits = redBits;
- if (greenBits < minBits)
- minBits = greenBits;
- if (blueBits < minBits)
- minBits = blueBits;
-
- if (((*(char*)&endianTest) == 0) != bigEndian)
- endianMismatch = true;
- else
- endianMismatch = false;
- }
-
- bool PixelFormat::isSane(void)
- {
- int totalBits;
-
- if ((bpp != 8) && (bpp != 16) && (bpp != 32))
- return false;
- if (depth > bpp)
- return false;
-
- if (!trueColour && (depth != 8))
- return false;
-
- if ((redMax & (redMax + 1)) != 0)
- return false;
- if ((greenMax & (greenMax + 1)) != 0)
- return false;
- if ((blueMax & (blueMax + 1)) != 0)
- return false;
-
- /*
- * We don't allow individual channels > 8 bits in order to keep our
- * conversions simple.
- */
- if (redMax >= (1 << 8))
- return false;
- if (greenMax >= (1 << 8))
- return false;
- if (blueMax >= (1 << 8))
- return false;
-
- totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
- if (totalBits > depth)
- return false;
-
- if ((bits(redMax) + redShift) > bpp)
- return false;
- if ((bits(greenMax) + greenShift) > bpp)
- return false;
- if ((bits(blueMax) + blueShift) > bpp)
- return false;
-
- if (((redMax << redShift) & (greenMax << greenShift)) != 0)
- return false;
- if (((redMax << redShift) & (blueMax << blueShift)) != 0)
- return false;
- if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
- return false;
-
- return true;
- }
-
- // Preprocessor generated, optimised methods
-
- #define INBPP 8
- #define OUTBPP 8
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 16
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 32
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #undef INBPP
-
- #define INBPP 16
- #define OUTBPP 8
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 16
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 32
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #undef INBPP
-
- #define INBPP 32
- #define OUTBPP 8
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 16
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #define OUTBPP 32
- #include "PixelFormatBPP.cxx"
- #undef OUTBPP
- #undef INBPP
|