123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009 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.
- */
- #include <assert.h>
- #include <stdio.h>
- #include <string.h>
- #include <rdr/InStream.h>
- #include <rdr/OutStream.h>
- #include <rfb/PixelFormat.h>
- #include <rfb/util.h>
-
- #ifdef _WIN32
- #define strcasecmp _stricmp
- #endif
-
- using namespace rfb;
-
- 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)
- {
- assert((bpp == 8) || (bpp == 16) || (bpp == 32));
- assert(depth <= bpp);
- assert((redMax & (redMax + 1)) == 0);
- assert((greenMax & (greenMax + 1)) == 0);
- assert((blueMax & (blueMax + 1)) == 0);
-
- 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
- {
- return (bpp == other.bpp &&
- depth == other.depth &&
- (bigEndian == other.bigEndian || bpp == 8) &&
- trueColour == other.trueColour &&
- (!trueColour || (redMax == other.redMax &&
- greenMax == other.greenMax &&
- blueMax == other.blueMax &&
- redShift == other.redShift &&
- greenShift == other.greenShift &&
- blueShift == other.blueShift)));
- }
-
- 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);
-
- 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;
-
- return true;
- }
-
-
- bool PixelFormat::isBigEndian(void) const
- {
- return bigEndian;
- }
-
-
- bool PixelFormat::isLittleEndian(void) const
- {
- return ! bigEndian;
- }
-
-
- Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue,
- ColourMap* cm) const
- {
- if (trueColour) {
- rdr::U32 r = ((rdr::U32)red * redMax + 32767) / 65535;
- rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535;
- rdr::U32 b = ((rdr::U32)blue * blueMax + 32767) / 65535;
-
- return (r << redShift) | (g << greenShift) | (b << blueShift);
- } else if (cm) {
- // Try to find the closest pixel by Cartesian distance
- int colours = 1 << depth;
- int diff = 256 * 256 * 4;
- int col = 0;
- for (int i=0; i<colours; i++) {
- int r, g, b;
- cm->lookup(i, &r, &g, &b);
- int rd = (r-red) >> 8;
- int gd = (g-green) >> 8;
- int bd = (b-blue) >> 8;
- int d = rd*rd + gd*gd + bd*bd;
- if (d < diff) {
- col = i;
- diff = d;
- }
- }
- return col;
- }
- // XXX just return 0 for colour map?
- return 0;
- }
-
-
- Pixel PixelFormat::pixelFromRGB(rdr::U8 red, rdr::U8 green, rdr::U8 blue,
- ColourMap* cm) const
- {
- if (trueColour) {
- rdr::U32 r = ((rdr::U32)red * redMax + 127) / 255;
- rdr::U32 g = ((rdr::U32)green * greenMax + 127) / 255;
- rdr::U32 b = ((rdr::U32)blue * blueMax + 127) / 255;
-
- return (r << redShift) | (g << greenShift) | (b << blueShift);
- }
-
- return pixelFromRGB((rdr::U16)(red << 8), (rdr::U16)(green << 8),
- (rdr::U16)(blue << 8), cm);
- }
-
-
- void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
- int pixels, ColourMap* cm) const
- {
- if (is888()) {
- // Optimised common case
- rdr::U8 *r, *g, *b;
-
- if (bigEndian) {
- r = dst + (24 - redShift)/8;
- g = dst + (24 - greenShift)/8;
- b = dst + (24 - blueShift)/8;
- } else {
- r = dst + redShift/8;
- g = dst + greenShift/8;
- b = dst + blueShift/8;
- }
-
- while (pixels--) {
- *r = *(src++);
- *g = *(src++);
- *b = *(src++);
- r += 4;
- g += 4;
- b += 4;
- }
- } else {
- // Generic code
- Pixel p;
- rdr::U8 r, g, b;
-
- while (pixels--) {
- r = *(src++);
- g = *(src++);
- b = *(src++);
-
- p = pixelFromRGB(r, g, b, cm);
-
- bufferFromPixel(dst, p);
- dst += bpp/8;
- }
- }
- }
-
-
- void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
- int w, int pitch, int h, ColourMap* cm) const
- {
- if (is888()) {
- // Optimised common case
- int rindex, gindex, bindex;
-
- if (bigEndian) {
- rindex = (24 - redShift)/8;
- gindex = (24 - greenShift)/8;
- bindex = (24 - blueShift)/8;
- } else {
- rindex = redShift/8;
- gindex = greenShift/8;
- bindex = blueShift/8;
- }
-
- int dstPad = pitch - w * 4;
- while (h > 0) {
- rdr::U8 *dstEndOfRow = (rdr::U8 *)dst + w * 4;
- while (dst < dstEndOfRow) {
- dst[rindex] = *(src++);
- dst[gindex] = *(src++);
- dst[bindex] = *(src++);
- dst += 4;
- }
- dst += dstPad;
- h--;
- }
- } else {
- // Generic code
- Pixel p;
- rdr::U8 r, g, b;
- int pixelSize = bpp/8;
-
- int dstPad = pitch - w * pixelSize;
- while (h > 0) {
- rdr::U8 *dstEndOfRow = (rdr::U8 *)dst + w * pixelSize;
- while (dst < dstEndOfRow) {
- r = *(src++);
- g = *(src++);
- b = *(src++);
-
- p = pixelFromRGB(r, g, b, cm);
-
- bufferFromPixel(dst, p);
- dst += pixelSize;
- }
- dst += dstPad;
- h--;
- }
- }
- }
-
-
- void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
- {
- rdr::U16 r, g, b;
-
- rgbFromPixel(p, cm, &r, &g, &b);
-
- rgb->r = r;
- rgb->g = g;
- rgb->b = b;
- }
-
-
- void PixelFormat::rgbFromBuffer(rdr::U16* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
- {
- Pixel p;
- rdr::U16 r, g, b;
-
- while (pixels--) {
- p = pixelFromBuffer(src);
- src += bpp/8;
-
- rgbFromPixel(p, cm, &r, &g, &b);
- *(dst++) = r;
- *(dst++) = g;
- *(dst++) = b;
- }
- }
-
-
- void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm) 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;
- }
-
- while (pixels--) {
- *(dst++) = *r;
- *(dst++) = *g;
- *(dst++) = *b;
- r += 4;
- g += 4;
- b += 4;
- }
- } else {
- // Generic code
- Pixel p;
- rdr::U8 r, g, b;
-
- while (pixels--) {
- p = pixelFromBuffer(src);
- src += bpp/8;
-
- rgbFromPixel(p, cm, &r, &g, &b);
- *(dst++) = r;
- *(dst++) = g;
- *(dst++) = b;
- }
- }
- }
-
-
- void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
- int w, int pitch, int h, ColourMap* cm) const
- {
- if (is888()) {
- // Optimised common case
- int rindex, gindex, bindex;
-
- if (bigEndian) {
- rindex = (24 - redShift)/8;
- gindex = (24 - greenShift)/8;
- bindex = (24 - blueShift)/8;
- } else {
- rindex = redShift/8;
- gindex = greenShift/8;
- bindex = blueShift/8;
- }
-
- int srcPad = pitch - w * 4;
- while (h > 0) {
- rdr::U8 *srcEndOfRow = (rdr::U8 *)src + w * 4;
- while (src < srcEndOfRow) {
- *(dst++) = src[rindex];
- *(dst++) = src[gindex];
- *(dst++) = src[bindex];
- src += 4;
- }
- src += srcPad;
- h--;
- }
- } else {
- // Generic code
- Pixel p;
- rdr::U8 r, g, b;
- int pixelSize = bpp/8;
-
- int srcPad = pitch - w * pixelSize;
- while (h > 0) {
- rdr::U8 *srcEndOfRow = (rdr::U8 *)src + w * pixelSize;
- while (src < srcEndOfRow) {
- p = pixelFromBuffer(src);
-
- rgbFromPixel(p, cm, &r, &g, &b);
- *(dst++) = r;
- *(dst++) = g;
- *(dst++) = b;
- src += pixelSize;
- }
- src += srcPad;
- h--;
- }
- }
- }
-
-
- 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;
- }
-
- 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 redBits, greenBits, blueBits;
- int endianTest = 1;
-
- redBits = bits(redMax);
- greenBits = bits(greenMax);
- blueBits = bits(blueMax);
-
- redConvShift = 16 - redBits;
- greenConvShift = 16 - greenBits;
- blueConvShift = 16 - blueBits;
-
- if (((*(char*)&endianTest) == 0) != bigEndian)
- endianMismatch = true;
- else
- endianMismatch = false;
- }
|