blob: 9e762d9d3368ff49656f028543e0837a906e4226 [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
35PixelFormat::PixelFormat(int b, int d, bool e, bool t,
36 int rm, int gm, int bm, int rs, int gs, int bs)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000037 : bpp(b), depth(d), trueColour(t), bigEndian(e),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038 redMax(rm), greenMax(gm), blueMax(bm),
39 redShift(rs), greenShift(gs), blueShift(bs)
40{
Pierre Ossman6655d962014-01-20 14:50:19 +010041 assert(isSane());
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000042
Pierre Ossman19dbca22009-04-21 17:30:45 +000043 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044}
45
46PixelFormat::PixelFormat()
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000047 : bpp(8), depth(8), trueColour(true), bigEndian(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000048 redMax(7), greenMax(7), blueMax(3),
49 redShift(0), greenShift(3), blueShift(6)
50{
Pierre Ossman19dbca22009-04-21 17:30:45 +000051 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052}
53
54bool PixelFormat::equal(const PixelFormat& other) const
55{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010056 if (bpp != other.bpp || depth != other.depth)
57 return false;
58
59 if (redMax != other.redMax)
60 return false;
61 if (greenMax != other.greenMax)
62 return false;
63 if (blueMax != other.blueMax)
64 return false;
65
66 // Endianness requires more care to determine compatibility
67 if (bigEndian == other.bigEndian || bpp == 8) {
68 if (redShift != other.redShift)
69 return false;
70 if (greenShift != other.greenShift)
71 return false;
72 if (blueShift != other.blueShift)
73 return false;
74 } else {
75 // Has to be the same byte for each channel
76 if (redShift/8 != (3 - other.redShift/8))
77 return false;
78 if (greenShift/8 != (3 - other.greenShift/8))
79 return false;
80 if (blueShift/8 != (3 - other.blueShift/8))
81 return false;
82
83 // And the same bit offset within the byte
84 if (redShift%8 != other.redShift%8)
85 return false;
86 if (greenShift%8 != other.greenShift%8)
87 return false;
88 if (blueShift%8 != other.blueShift%8)
89 return false;
90
91 // And not cross a byte boundary
92 if (redShift/8 != (redShift + redBits - 1)/8)
93 return false;
94 if (greenShift/8 != (greenShift + greenBits - 1)/8)
95 return false;
96 if (blueShift/8 != (blueShift + blueBits - 1)/8)
97 return false;
98 }
99
100 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101}
102
103void PixelFormat::read(rdr::InStream* is)
104{
105 bpp = is->readU8();
106 depth = is->readU8();
107 bigEndian = is->readU8();
108 trueColour = is->readU8();
109 redMax = is->readU16();
110 greenMax = is->readU16();
111 blueMax = is->readU16();
112 redShift = is->readU8();
113 greenShift = is->readU8();
114 blueShift = is->readU8();
115 is->skip(3);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000116
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100117 // We have no real support for colour maps. If the client
118 // wants one, then we force a 8-bit true colour format and
119 // pretend it's a colour map.
120 if (!trueColour) {
121 redMax = 7;
122 greenMax = 7;
123 blueMax = 3;
124 redShift = 0;
125 greenShift = 3;
126 blueShift = 6;
127 }
128
Pierre Ossman6655d962014-01-20 14:50:19 +0100129 if (!isSane())
130 throw Exception("invalid pixel format");
131
Pierre Ossman19dbca22009-04-21 17:30:45 +0000132 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000133}
134
135void PixelFormat::write(rdr::OutStream* os) const
136{
137 os->writeU8(bpp);
138 os->writeU8(depth);
139 os->writeU8(bigEndian);
140 os->writeU8(trueColour);
141 os->writeU16(redMax);
142 os->writeU16(greenMax);
143 os->writeU16(blueMax);
144 os->writeU8(redShift);
145 os->writeU8(greenShift);
146 os->writeU8(blueShift);
147 os->pad(3);
148}
149
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000150
151bool PixelFormat::is888(void) const
152{
Pierre Ossman6ba9e1a2009-03-25 12:27:38 +0000153 if (!trueColour)
154 return false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000155 if (bpp != 32)
156 return false;
157 if (depth != 24)
158 return false;
159 if (redMax != 255)
160 return false;
161 if (greenMax != 255)
162 return false;
163 if (blueMax != 255)
164 return false;
165
166 return true;
167}
168
169
170bool PixelFormat::isBigEndian(void) const
171{
172 return bigEndian;
173}
174
175
176bool PixelFormat::isLittleEndian(void) const
177{
178 return ! bigEndian;
179}
180
181
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100182void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src, int pixels) const
Pierre Ossman19501b82009-03-31 14:06:53 +0000183{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100184 bufferFromRGB(dst, src, pixels, pixels, 1);
Pierre Ossman19501b82009-03-31 14:06:53 +0000185}
186
DRC33c15e32011-11-03 18:49:21 +0000187void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100188 int w, int stride, int h) const
DRC33c15e32011-11-03 18:49:21 +0000189{
190 if (is888()) {
191 // Optimised common case
Pierre Ossman8432ec12014-01-20 17:11:19 +0100192 rdr::U8 *r, *g, *b, *x;
DRC33c15e32011-11-03 18:49:21 +0000193
194 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100195 r = dst + (24 - redShift)/8;
196 g = dst + (24 - greenShift)/8;
197 b = dst + (24 - blueShift)/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100198 x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
DRC33c15e32011-11-03 18:49:21 +0000199 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100200 r = dst + redShift/8;
201 g = dst + greenShift/8;
202 b = dst + blueShift/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100203 x = dst + (48 - redShift - greenShift - blueShift)/8;
DRC33c15e32011-11-03 18:49:21 +0000204 }
205
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100206 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100207 while (h--) {
208 int w_ = w;
209 while (w_--) {
210 *r = *(src++);
211 *g = *(src++);
212 *b = *(src++);
Pierre Ossman8432ec12014-01-20 17:11:19 +0100213 *x = 0;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100214 r += 4;
215 g += 4;
216 b += 4;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100217 x += 4;
DRC33c15e32011-11-03 18:49:21 +0000218 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100219 r += dstPad;
220 g += dstPad;
221 b += dstPad;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100222 x += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000223 }
224 } else {
225 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100226 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100227 while (h--) {
228 int w_ = w;
229 while (w_--) {
230 Pixel p;
231 rdr::U8 r, g, b;
DRC33c15e32011-11-03 18:49:21 +0000232
DRC33c15e32011-11-03 18:49:21 +0000233 r = *(src++);
234 g = *(src++);
235 b = *(src++);
236
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100237 p = pixelFromRGB(r, g, b);
DRC33c15e32011-11-03 18:49:21 +0000238
239 bufferFromPixel(dst, p);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100240 dst += bpp/8;
DRC33c15e32011-11-03 18:49:21 +0000241 }
242 dst += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000243 }
244 }
245}
246
247
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100248void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels) const
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000249{
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100250 rgbFromBuffer(dst, src, pixels, pixels, 1);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000251}
252
253
DRCffe09d62011-08-17 02:27:59 +0000254void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100255 int w, int stride, int h) const
DRCffe09d62011-08-17 02:27:59 +0000256{
DRCffe09d62011-08-17 02:27:59 +0000257 if (is888()) {
258 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100259 const rdr::U8 *r, *g, *b;
DRCffe09d62011-08-17 02:27:59 +0000260
261 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100262 r = src + (24 - redShift)/8;
263 g = src + (24 - greenShift)/8;
264 b = src + (24 - blueShift)/8;
DRCffe09d62011-08-17 02:27:59 +0000265 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100266 r = src + redShift/8;
267 g = src + greenShift/8;
268 b = src + blueShift/8;
DRCffe09d62011-08-17 02:27:59 +0000269 }
270
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100271 int srcPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100272 while (h--) {
273 int w_ = w;
274 while (w_--) {
275 *(dst++) = *r;
276 *(dst++) = *g;
277 *(dst++) = *b;
278 r += 4;
279 g += 4;
280 b += 4;
DRCffe09d62011-08-17 02:27:59 +0000281 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100282 r += srcPad;
283 g += srcPad;
284 b += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000285 }
286 } else {
287 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100288 int srcPad = (stride - w) * bpp/8;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100289 while (h--) {
290 int w_ = w;
291 while (w_--) {
292 Pixel p;
293 rdr::U8 r, g, b;
DRCffe09d62011-08-17 02:27:59 +0000294
DRCbf79f682011-08-19 16:08:09 +0000295 p = pixelFromBuffer(src);
DRCffe09d62011-08-17 02:27:59 +0000296
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100297 rgbFromPixel(p, &r, &g, &b);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100298
DRCffe09d62011-08-17 02:27:59 +0000299 *(dst++) = r;
300 *(dst++) = g;
301 *(dst++) = b;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100302 src += bpp/8;
DRCffe09d62011-08-17 02:27:59 +0000303 }
DRCbf79f682011-08-19 16:08:09 +0000304 src += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000305 }
306 }
307}
308
309
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310void PixelFormat::print(char* str, int len) const
311{
312 // Unfortunately snprintf is not widely available so we build the string up
313 // using strncat - not pretty, but should be safe against buffer overruns.
314
315 char num[20];
316 if (len < 1) return;
317 str[0] = 0;
318 strncat(str, "depth ", len-1-strlen(str));
319 sprintf(num,"%d",depth);
320 strncat(str, num, len-1-strlen(str));
321 strncat(str, " (", len-1-strlen(str));
322 sprintf(num,"%d",bpp);
323 strncat(str, num, len-1-strlen(str));
324 strncat(str, "bpp)", len-1-strlen(str));
325 if (bpp != 8) {
326 if (bigEndian)
327 strncat(str, " big-endian", len-1-strlen(str));
328 else
329 strncat(str, " little-endian", len-1-strlen(str));
330 }
331
332 if (!trueColour) {
333 strncat(str, " color-map", len-1-strlen(str));
334 return;
335 }
336
337 if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
338 blueMax == (1 << greenShift) - 1 &&
339 greenMax == (1 << (redShift-greenShift)) - 1 &&
340 redMax == (1 << (depth-redShift)) - 1)
341 {
342 strncat(str, " rgb", len-1-strlen(str));
343 sprintf(num,"%d",depth-redShift);
344 strncat(str, num, len-1-strlen(str));
345 sprintf(num,"%d",redShift-greenShift);
346 strncat(str, num, len-1-strlen(str));
347 sprintf(num,"%d",greenShift);
348 strncat(str, num, len-1-strlen(str));
349 return;
350 }
351
352 if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
353 redMax == (1 << greenShift) - 1 &&
354 greenMax == (1 << (blueShift-greenShift)) - 1 &&
355 blueMax == (1 << (depth-blueShift)) - 1)
356 {
357 strncat(str, " bgr", len-1-strlen(str));
358 sprintf(num,"%d",depth-blueShift);
359 strncat(str, num, len-1-strlen(str));
360 sprintf(num,"%d",blueShift-greenShift);
361 strncat(str, num, len-1-strlen(str));
362 sprintf(num,"%d",greenShift);
363 strncat(str, num, len-1-strlen(str));
364 return;
365 }
366
367 strncat(str, " rgb max ", len-1-strlen(str));
368 sprintf(num,"%d,",redMax);
369 strncat(str, num, len-1-strlen(str));
370 sprintf(num,"%d,",greenMax);
371 strncat(str, num, len-1-strlen(str));
372 sprintf(num,"%d",blueMax);
373 strncat(str, num, len-1-strlen(str));
374 strncat(str, " shift ", len-1-strlen(str));
375 sprintf(num,"%d,",redShift);
376 strncat(str, num, len-1-strlen(str));
377 sprintf(num,"%d,",greenShift);
378 strncat(str, num, len-1-strlen(str));
379 sprintf(num,"%d",blueShift);
380 strncat(str, num, len-1-strlen(str));
381}
382
383
384bool PixelFormat::parse(const char* str)
385{
386 char rgbbgr[4];
387 int bits1, bits2, bits3;
388 if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
389 return false;
390
391 depth = bits1 + bits2 + bits3;
392 bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
393 trueColour = true;
394 rdr::U32 endianTest = 1;
395 bigEndian = (*(rdr::U8*)&endianTest == 0);
396
397 greenShift = bits3;
398 greenMax = (1 << bits2) - 1;
399
400 if (strcasecmp(rgbbgr, "bgr") == 0) {
401 redShift = 0;
402 redMax = (1 << bits3) - 1;
403 blueShift = bits3 + bits2;
404 blueMax = (1 << bits1) - 1;
405 } else if (strcasecmp(rgbbgr, "rgb") == 0) {
406 blueShift = 0;
407 blueMax = (1 << bits3) - 1;
408 redShift = bits3 + bits2;
409 redMax = (1 << bits1) - 1;
410 } else {
411 return false;
412 }
Pierre Ossman430db3d2009-04-03 12:49:38 +0000413
Pierre Ossman6655d962014-01-20 14:50:19 +0100414 assert(isSane());
415
Pierre Ossman19dbca22009-04-21 17:30:45 +0000416 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000417
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000418 return true;
419}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000420
421
422static int bits(rdr::U16 value)
423{
424 int bits;
425
426 bits = 16;
427
428 if (!(value & 0xff00)) {
429 bits -= 8;
430 value <<= 8;
431 }
432 if (!(value & 0xf000)) {
433 bits -= 4;
434 value <<= 4;
435 }
436 if (!(value & 0xc000)) {
437 bits -= 2;
438 value <<= 2;
439 }
440 if (!(value & 0x8000)) {
441 bits -= 1;
442 value <<= 1;
443 }
444
445 return bits;
446}
447
Pierre Ossman19dbca22009-04-21 17:30:45 +0000448void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000449{
Pierre Ossman19dbca22009-04-21 17:30:45 +0000450 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000451
452 redBits = bits(redMax);
453 greenBits = bits(greenMax);
454 blueBits = bits(blueMax);
455
Pierre Ossman6e5cd5d2014-02-28 11:54:34 +0100456 maxBits = redBits;
457 if (greenBits > maxBits)
458 maxBits = greenBits;
459 if (blueBits > maxBits)
460 maxBits = blueBits;
461
462 minBits = redBits;
463 if (greenBits < minBits)
464 minBits = greenBits;
465 if (blueBits < minBits)
466 minBits = blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000467
468 if (((*(char*)&endianTest) == 0) != bigEndian)
469 endianMismatch = true;
470 else
471 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000472}
Pierre Ossman6655d962014-01-20 14:50:19 +0100473
474bool PixelFormat::isSane(void)
475{
476 int totalBits;
477
478 if ((bpp != 8) && (bpp != 16) && (bpp != 32))
479 return false;
480 if (depth > bpp)
481 return false;
482
483 if (!trueColour && (depth != 8))
484 return false;
485
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100486 if ((redMax & (redMax + 1)) != 0)
487 return false;
488 if ((greenMax & (greenMax + 1)) != 0)
489 return false;
490 if ((blueMax & (blueMax + 1)) != 0)
491 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100492
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100493 /*
494 * We don't allow individual channels > 8 bits in order to keep our
495 * conversions simple.
496 */
497 if (redMax >= (1 << 8))
498 return false;
499 if (greenMax >= (1 << 8))
500 return false;
501 if (blueMax >= (1 << 8))
502 return false;
Pierre Ossman8b874e42014-01-20 17:23:51 +0100503
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100504 totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
505 if (totalBits > bpp)
506 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100507
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100508 if (((redMax << redShift) & (greenMax << greenShift)) != 0)
509 return false;
510 if (((redMax << redShift) & (blueMax << blueShift)) != 0)
511 return false;
512 if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
513 return false;
Pierre Ossman6655d962014-01-20 14:50:19 +0100514
515 return true;
516}