blob: 366c0a339f036d7792794652ed844c1dd705997b [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
DRCffe09d62011-08-17 02:27:59 +00002 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
Pierre Ossman6655d962014-01-20 14:50:19 +01003 * Copyright 2009-2014 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
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 */
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000020#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <stdio.h>
22#include <string.h>
23#include <rdr/InStream.h>
24#include <rdr/OutStream.h>
Pierre Ossman6655d962014-01-20 14:50:19 +010025#include <rfb/Exception.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/PixelFormat.h>
27#include <rfb/util.h>
28
29#ifdef _WIN32
30#define strcasecmp _stricmp
31#endif
32
33using namespace rfb;
34
Pierre Ossman2fe68da2014-07-08 15:06:25 +020035rdr::U8 PixelFormat::upconvTable[256*8];
36
37class PixelFormat::Init {
38public:
39 Init();
40};
41
42PixelFormat::Init PixelFormat::_init;
43
44
45PixelFormat::Init::Init()
46{
47 int bits;
48
49 // Bit replication is almost perfect, but not quite. And
50 // a lookup table is still quicker when there is a large
51 // difference between the source and destination depth.
52
53 for (bits = 1;bits <= 8;bits++) {
54 int i, maxVal;
55 rdr::U8 *subTable;
56
57 maxVal = (1 << bits) - 1;
58 subTable = &upconvTable[(bits-1)*256];
59
60 for (i = 0;i <= maxVal;i++)
61 subTable[i] = i * 255 / maxVal;
62
63 // Duplicate the table so that we don't have to care about
64 // the upper bits when doing a lookup
65 for (;i < 256;i += maxVal+1)
66 memcpy(&subTable[i], &subTable[0], maxVal+1);
67 }
68}
69
70
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071PixelFormat::PixelFormat(int b, int d, bool e, bool t,
72 int rm, int gm, int bm, int rs, int gs, int bs)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000073 : bpp(b), depth(d), trueColour(t), bigEndian(e),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000074 redMax(rm), greenMax(gm), blueMax(bm),
75 redShift(rs), greenShift(gs), blueShift(bs)
76{
Pierre Ossman6655d962014-01-20 14:50:19 +010077 assert(isSane());
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000078
Pierre Ossman19dbca22009-04-21 17:30:45 +000079 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080}
81
82PixelFormat::PixelFormat()
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000083 : bpp(8), depth(8), trueColour(true), bigEndian(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084 redMax(7), greenMax(7), blueMax(3),
85 redShift(0), greenShift(3), blueShift(6)
86{
Pierre Ossman19dbca22009-04-21 17:30:45 +000087 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000088}
89
90bool PixelFormat::equal(const PixelFormat& other) const
91{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010092 if (bpp != other.bpp || depth != other.depth)
93 return false;
94
95 if (redMax != other.redMax)
96 return false;
97 if (greenMax != other.greenMax)
98 return false;
99 if (blueMax != other.blueMax)
100 return false;
101
102 // Endianness requires more care to determine compatibility
103 if (bigEndian == other.bigEndian || bpp == 8) {
104 if (redShift != other.redShift)
105 return false;
106 if (greenShift != other.greenShift)
107 return false;
108 if (blueShift != other.blueShift)
109 return false;
110 } else {
111 // Has to be the same byte for each channel
112 if (redShift/8 != (3 - other.redShift/8))
113 return false;
114 if (greenShift/8 != (3 - other.greenShift/8))
115 return false;
116 if (blueShift/8 != (3 - other.blueShift/8))
117 return false;
118
119 // And the same bit offset within the byte
120 if (redShift%8 != other.redShift%8)
121 return false;
122 if (greenShift%8 != other.greenShift%8)
123 return false;
124 if (blueShift%8 != other.blueShift%8)
125 return false;
126
127 // And not cross a byte boundary
128 if (redShift/8 != (redShift + redBits - 1)/8)
129 return false;
130 if (greenShift/8 != (greenShift + greenBits - 1)/8)
131 return false;
132 if (blueShift/8 != (blueShift + blueBits - 1)/8)
133 return false;
134 }
135
136 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000137}
138
139void PixelFormat::read(rdr::InStream* is)
140{
141 bpp = is->readU8();
142 depth = is->readU8();
143 bigEndian = is->readU8();
144 trueColour = is->readU8();
145 redMax = is->readU16();
146 greenMax = is->readU16();
147 blueMax = is->readU16();
148 redShift = is->readU8();
149 greenShift = is->readU8();
150 blueShift = is->readU8();
151 is->skip(3);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000152
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100153 // We have no real support for colour maps. If the client
154 // wants one, then we force a 8-bit true colour format and
155 // pretend it's a colour map.
156 if (!trueColour) {
157 redMax = 7;
158 greenMax = 7;
159 blueMax = 3;
160 redShift = 0;
161 greenShift = 3;
162 blueShift = 6;
163 }
164
Pierre Ossman6655d962014-01-20 14:50:19 +0100165 if (!isSane())
166 throw Exception("invalid pixel format");
167
Pierre Ossman19dbca22009-04-21 17:30:45 +0000168 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169}
170
171void PixelFormat::write(rdr::OutStream* os) const
172{
173 os->writeU8(bpp);
174 os->writeU8(depth);
175 os->writeU8(bigEndian);
176 os->writeU8(trueColour);
177 os->writeU16(redMax);
178 os->writeU16(greenMax);
179 os->writeU16(blueMax);
180 os->writeU8(redShift);
181 os->writeU8(greenShift);
182 os->writeU8(blueShift);
183 os->pad(3);
184}
185
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000186
187bool PixelFormat::is888(void) const
188{
Pierre Ossman6ba9e1a2009-03-25 12:27:38 +0000189 if (!trueColour)
190 return false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000191 if (bpp != 32)
192 return false;
193 if (depth != 24)
194 return false;
195 if (redMax != 255)
196 return false;
197 if (greenMax != 255)
198 return false;
199 if (blueMax != 255)
200 return false;
201
202 return true;
203}
204
205
206bool PixelFormat::isBigEndian(void) const
207{
208 return bigEndian;
209}
210
211
212bool PixelFormat::isLittleEndian(void) const
213{
214 return ! bigEndian;
215}
216
217
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100218void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src, int pixels) const
Pierre Ossman19501b82009-03-31 14:06:53 +0000219{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100220 bufferFromRGB(dst, src, pixels, pixels, 1);
Pierre Ossman19501b82009-03-31 14:06:53 +0000221}
222
DRC33c15e32011-11-03 18:49:21 +0000223void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100224 int w, int stride, int h) const
DRC33c15e32011-11-03 18:49:21 +0000225{
226 if (is888()) {
227 // Optimised common case
Pierre Ossman8432ec12014-01-20 17:11:19 +0100228 rdr::U8 *r, *g, *b, *x;
DRC33c15e32011-11-03 18:49:21 +0000229
230 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100231 r = dst + (24 - redShift)/8;
232 g = dst + (24 - greenShift)/8;
233 b = dst + (24 - blueShift)/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100234 x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
DRC33c15e32011-11-03 18:49:21 +0000235 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100236 r = dst + redShift/8;
237 g = dst + greenShift/8;
238 b = dst + blueShift/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100239 x = dst + (48 - redShift - greenShift - blueShift)/8;
DRC33c15e32011-11-03 18:49:21 +0000240 }
241
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100242 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100243 while (h--) {
244 int w_ = w;
245 while (w_--) {
246 *r = *(src++);
247 *g = *(src++);
248 *b = *(src++);
Pierre Ossman8432ec12014-01-20 17:11:19 +0100249 *x = 0;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100250 r += 4;
251 g += 4;
252 b += 4;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100253 x += 4;
DRC33c15e32011-11-03 18:49:21 +0000254 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100255 r += dstPad;
256 g += dstPad;
257 b += dstPad;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100258 x += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000259 }
260 } else {
261 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100262 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100263 while (h--) {
264 int w_ = w;
265 while (w_--) {
266 Pixel p;
267 rdr::U8 r, g, b;
DRC33c15e32011-11-03 18:49:21 +0000268
DRC33c15e32011-11-03 18:49:21 +0000269 r = *(src++);
270 g = *(src++);
271 b = *(src++);
272
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100273 p = pixelFromRGB(r, g, b);
DRC33c15e32011-11-03 18:49:21 +0000274
275 bufferFromPixel(dst, p);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100276 dst += bpp/8;
DRC33c15e32011-11-03 18:49:21 +0000277 }
278 dst += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000279 }
280 }
281}
282
283
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100284void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels) const
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000285{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100286 rgbFromBuffer(dst, src, pixels, pixels, 1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000287}
288
289
DRCffe09d62011-08-17 02:27:59 +0000290void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100291 int w, int stride, int h) const
DRCffe09d62011-08-17 02:27:59 +0000292{
DRCffe09d62011-08-17 02:27:59 +0000293 if (is888()) {
294 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100295 const rdr::U8 *r, *g, *b;
DRCffe09d62011-08-17 02:27:59 +0000296
297 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100298 r = src + (24 - redShift)/8;
299 g = src + (24 - greenShift)/8;
300 b = src + (24 - blueShift)/8;
DRCffe09d62011-08-17 02:27:59 +0000301 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100302 r = src + redShift/8;
303 g = src + greenShift/8;
304 b = src + blueShift/8;
DRCffe09d62011-08-17 02:27:59 +0000305 }
306
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100307 int srcPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100308 while (h--) {
309 int w_ = w;
310 while (w_--) {
311 *(dst++) = *r;
312 *(dst++) = *g;
313 *(dst++) = *b;
314 r += 4;
315 g += 4;
316 b += 4;
DRCffe09d62011-08-17 02:27:59 +0000317 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100318 r += srcPad;
319 g += srcPad;
320 b += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000321 }
322 } else {
323 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100324 int srcPad = (stride - w) * bpp/8;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100325 while (h--) {
326 int w_ = w;
327 while (w_--) {
328 Pixel p;
329 rdr::U8 r, g, b;
DRCffe09d62011-08-17 02:27:59 +0000330
DRCbf79f682011-08-19 16:08:09 +0000331 p = pixelFromBuffer(src);
DRCffe09d62011-08-17 02:27:59 +0000332
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100333 rgbFromPixel(p, &r, &g, &b);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100334
DRCffe09d62011-08-17 02:27:59 +0000335 *(dst++) = r;
336 *(dst++) = g;
337 *(dst++) = b;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100338 src += bpp/8;
DRCffe09d62011-08-17 02:27:59 +0000339 }
DRCbf79f682011-08-19 16:08:09 +0000340 src += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000341 }
342 }
343}
344
345
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000346void PixelFormat::print(char* str, int len) const
347{
348 // Unfortunately snprintf is not widely available so we build the string up
349 // using strncat - not pretty, but should be safe against buffer overruns.
350
351 char num[20];
352 if (len < 1) return;
353 str[0] = 0;
354 strncat(str, "depth ", len-1-strlen(str));
355 sprintf(num,"%d",depth);
356 strncat(str, num, len-1-strlen(str));
357 strncat(str, " (", len-1-strlen(str));
358 sprintf(num,"%d",bpp);
359 strncat(str, num, len-1-strlen(str));
360 strncat(str, "bpp)", len-1-strlen(str));
361 if (bpp != 8) {
362 if (bigEndian)
363 strncat(str, " big-endian", len-1-strlen(str));
364 else
365 strncat(str, " little-endian", len-1-strlen(str));
366 }
367
368 if (!trueColour) {
369 strncat(str, " color-map", len-1-strlen(str));
370 return;
371 }
372
373 if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
374 blueMax == (1 << greenShift) - 1 &&
375 greenMax == (1 << (redShift-greenShift)) - 1 &&
376 redMax == (1 << (depth-redShift)) - 1)
377 {
378 strncat(str, " rgb", len-1-strlen(str));
379 sprintf(num,"%d",depth-redShift);
380 strncat(str, num, len-1-strlen(str));
381 sprintf(num,"%d",redShift-greenShift);
382 strncat(str, num, len-1-strlen(str));
383 sprintf(num,"%d",greenShift);
384 strncat(str, num, len-1-strlen(str));
385 return;
386 }
387
388 if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
389 redMax == (1 << greenShift) - 1 &&
390 greenMax == (1 << (blueShift-greenShift)) - 1 &&
391 blueMax == (1 << (depth-blueShift)) - 1)
392 {
393 strncat(str, " bgr", len-1-strlen(str));
394 sprintf(num,"%d",depth-blueShift);
395 strncat(str, num, len-1-strlen(str));
396 sprintf(num,"%d",blueShift-greenShift);
397 strncat(str, num, len-1-strlen(str));
398 sprintf(num,"%d",greenShift);
399 strncat(str, num, len-1-strlen(str));
400 return;
401 }
402
403 strncat(str, " rgb max ", len-1-strlen(str));
404 sprintf(num,"%d,",redMax);
405 strncat(str, num, len-1-strlen(str));
406 sprintf(num,"%d,",greenMax);
407 strncat(str, num, len-1-strlen(str));
408 sprintf(num,"%d",blueMax);
409 strncat(str, num, len-1-strlen(str));
410 strncat(str, " shift ", len-1-strlen(str));
411 sprintf(num,"%d,",redShift);
412 strncat(str, num, len-1-strlen(str));
413 sprintf(num,"%d,",greenShift);
414 strncat(str, num, len-1-strlen(str));
415 sprintf(num,"%d",blueShift);
416 strncat(str, num, len-1-strlen(str));
417}
418
419
420bool PixelFormat::parse(const char* str)
421{
422 char rgbbgr[4];
423 int bits1, bits2, bits3;
424 if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
425 return false;
426
427 depth = bits1 + bits2 + bits3;
428 bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
429 trueColour = true;
430 rdr::U32 endianTest = 1;
431 bigEndian = (*(rdr::U8*)&endianTest == 0);
432
433 greenShift = bits3;
434 greenMax = (1 << bits2) - 1;
435
436 if (strcasecmp(rgbbgr, "bgr") == 0) {
437 redShift = 0;
438 redMax = (1 << bits3) - 1;
439 blueShift = bits3 + bits2;
440 blueMax = (1 << bits1) - 1;
441 } else if (strcasecmp(rgbbgr, "rgb") == 0) {
442 blueShift = 0;
443 blueMax = (1 << bits3) - 1;
444 redShift = bits3 + bits2;
445 redMax = (1 << bits1) - 1;
446 } else {
447 return false;
448 }
Pierre Ossman430db3d2009-04-03 12:49:38 +0000449
Pierre Ossman6655d962014-01-20 14:50:19 +0100450 assert(isSane());
451
Pierre Ossman19dbca22009-04-21 17:30:45 +0000452 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000453
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000454 return true;
455}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000456
457
458static int bits(rdr::U16 value)
459{
460 int bits;
461
462 bits = 16;
463
464 if (!(value & 0xff00)) {
465 bits -= 8;
466 value <<= 8;
467 }
468 if (!(value & 0xf000)) {
469 bits -= 4;
470 value <<= 4;
471 }
472 if (!(value & 0xc000)) {
473 bits -= 2;
474 value <<= 2;
475 }
476 if (!(value & 0x8000)) {
477 bits -= 1;
478 value <<= 1;
479 }
480
481 return bits;
482}
483
Pierre Ossman19dbca22009-04-21 17:30:45 +0000484void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000485{
Pierre Ossman19dbca22009-04-21 17:30:45 +0000486 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000487
488 redBits = bits(redMax);
489 greenBits = bits(greenMax);
490 blueBits = bits(blueMax);
491
Pierre Ossman6e5cd5d2014-02-28 11:54:34 +0100492 maxBits = redBits;
493 if (greenBits > maxBits)
494 maxBits = greenBits;
495 if (blueBits > maxBits)
496 maxBits = blueBits;
497
498 minBits = redBits;
499 if (greenBits < minBits)
500 minBits = greenBits;
501 if (blueBits < minBits)
502 minBits = blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000503
504 if (((*(char*)&endianTest) == 0) != bigEndian)
505 endianMismatch = true;
506 else
507 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000508}
Pierre Ossman6655d962014-01-20 14:50:19 +0100509
510bool PixelFormat::isSane(void)
511{
512 int totalBits;
513
514 if ((bpp != 8) && (bpp != 16) && (bpp != 32))
515 return false;
516 if (depth > bpp)
517 return false;
518
519 if (!trueColour && (depth != 8))
520 return false;
521
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100522 if ((redMax & (redMax + 1)) != 0)
523 return false;
524 if ((greenMax & (greenMax + 1)) != 0)
525 return false;
526 if ((blueMax & (blueMax + 1)) != 0)
527 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100528
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100529 /*
530 * We don't allow individual channels > 8 bits in order to keep our
531 * conversions simple.
532 */
533 if (redMax >= (1 << 8))
534 return false;
535 if (greenMax >= (1 << 8))
536 return false;
537 if (blueMax >= (1 << 8))
538 return false;
Pierre Ossman8b874e42014-01-20 17:23:51 +0100539
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100540 totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
541 if (totalBits > bpp)
542 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100543
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100544 if (((redMax << redShift) & (greenMax << greenShift)) != 0)
545 return false;
546 if (((redMax << redShift) & (blueMax << blueShift)) != 0)
547 return false;
548 if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
549 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100550
551 return true;
552}