blob: 2883f621018a101745353d9a6cf9786e2c8f96ff [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{
56 return (bpp == other.bpp &&
57 depth == other.depth &&
58 (bigEndian == other.bigEndian || bpp == 8) &&
59 trueColour == other.trueColour &&
60 (!trueColour || (redMax == other.redMax &&
61 greenMax == other.greenMax &&
62 blueMax == other.blueMax &&
63 redShift == other.redShift &&
64 greenShift == other.greenShift &&
65 blueShift == other.blueShift)));
66}
67
68void PixelFormat::read(rdr::InStream* is)
69{
70 bpp = is->readU8();
71 depth = is->readU8();
72 bigEndian = is->readU8();
73 trueColour = is->readU8();
74 redMax = is->readU16();
75 greenMax = is->readU16();
76 blueMax = is->readU16();
77 redShift = is->readU8();
78 greenShift = is->readU8();
79 blueShift = is->readU8();
80 is->skip(3);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000081
Pierre Ossman6655d962014-01-20 14:50:19 +010082 if (!isSane())
83 throw Exception("invalid pixel format");
84
Pierre Ossman19dbca22009-04-21 17:30:45 +000085 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086}
87
88void PixelFormat::write(rdr::OutStream* os) const
89{
90 os->writeU8(bpp);
91 os->writeU8(depth);
92 os->writeU8(bigEndian);
93 os->writeU8(trueColour);
94 os->writeU16(redMax);
95 os->writeU16(greenMax);
96 os->writeU16(blueMax);
97 os->writeU8(redShift);
98 os->writeU8(greenShift);
99 os->writeU8(blueShift);
100 os->pad(3);
101}
102
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000103
104bool PixelFormat::is888(void) const
105{
Pierre Ossman6ba9e1a2009-03-25 12:27:38 +0000106 if (!trueColour)
107 return false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000108 if (bpp != 32)
109 return false;
110 if (depth != 24)
111 return false;
112 if (redMax != 255)
113 return false;
114 if (greenMax != 255)
115 return false;
116 if (blueMax != 255)
117 return false;
118
119 return true;
120}
121
122
123bool PixelFormat::isBigEndian(void) const
124{
125 return bigEndian;
126}
127
128
129bool PixelFormat::isLittleEndian(void) const
130{
131 return ! bigEndian;
132}
133
134
Pierre Ossman19501b82009-03-31 14:06:53 +0000135void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
136 int pixels, ColourMap* cm) const
137{
138 if (is888()) {
139 // Optimised common case
140 rdr::U8 *r, *g, *b;
141
Pierre Ossmanf61db642010-10-13 10:45:21 +0000142 if (bigEndian) {
143 r = dst + (24 - redShift)/8;
144 g = dst + (24 - greenShift)/8;
145 b = dst + (24 - blueShift)/8;
146 } else {
147 r = dst + redShift/8;
148 g = dst + greenShift/8;
149 b = dst + blueShift/8;
150 }
Pierre Ossman19501b82009-03-31 14:06:53 +0000151
152 while (pixels--) {
153 *r = *(src++);
154 *g = *(src++);
155 *b = *(src++);
156 r += 4;
157 g += 4;
158 b += 4;
159 }
160 } else {
161 // Generic code
162 Pixel p;
163 rdr::U8 r, g, b;
164
165 while (pixels--) {
166 r = *(src++);
167 g = *(src++);
168 b = *(src++);
169
170 p = pixelFromRGB(r, g, b, cm);
171
172 bufferFromPixel(dst, p);
173 dst += bpp/8;
174 }
175 }
176}
177
DRC33c15e32011-11-03 18:49:21 +0000178void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
179 int w, int pitch, int h, ColourMap* cm) const
180{
181 if (is888()) {
182 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100183 rdr::U8 *r, *g, *b;
DRC33c15e32011-11-03 18:49:21 +0000184
185 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100186 r = dst + (24 - redShift)/8;
187 g = dst + (24 - greenShift)/8;
188 b = dst + (24 - blueShift)/8;
DRC33c15e32011-11-03 18:49:21 +0000189 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100190 r = dst + redShift/8;
191 g = dst + greenShift/8;
192 b = dst + blueShift/8;
DRC33c15e32011-11-03 18:49:21 +0000193 }
194
195 int dstPad = pitch - w * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100196 while (h--) {
197 int w_ = w;
198 while (w_--) {
199 *r = *(src++);
200 *g = *(src++);
201 *b = *(src++);
202 r += 4;
203 g += 4;
204 b += 4;
DRC33c15e32011-11-03 18:49:21 +0000205 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100206 r += dstPad;
207 g += dstPad;
208 b += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000209 }
210 } else {
211 // Generic code
Pierre Ossman2baf7022014-01-20 16:40:10 +0100212 int dstPad = pitch - w * 4;
213 while (h--) {
214 int w_ = w;
215 while (w_--) {
216 Pixel p;
217 rdr::U8 r, g, b;
DRC33c15e32011-11-03 18:49:21 +0000218
DRC33c15e32011-11-03 18:49:21 +0000219 r = *(src++);
220 g = *(src++);
221 b = *(src++);
222
223 p = pixelFromRGB(r, g, b, cm);
224
225 bufferFromPixel(dst, p);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100226 dst += bpp/8;
DRC33c15e32011-11-03 18:49:21 +0000227 }
228 dst += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000229 }
230 }
231}
232
233
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000234void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
235{
236 rdr::U16 r, g, b;
237
238 rgbFromPixel(p, cm, &r, &g, &b);
239
240 rgb->r = r;
241 rgb->g = g;
242 rgb->b = b;
243}
244
245
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000246void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
247{
Pierre Ossman38a6be42009-03-25 12:26:41 +0000248 if (is888()) {
249 // Optimised common case
250 const rdr::U8 *r, *g, *b;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000251
Pierre Ossmanf61db642010-10-13 10:45:21 +0000252 if (bigEndian) {
253 r = src + (24 - redShift)/8;
254 g = src + (24 - greenShift)/8;
255 b = src + (24 - blueShift)/8;
256 } else {
257 r = src + redShift/8;
258 g = src + greenShift/8;
259 b = src + blueShift/8;
260 }
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000261
Pierre Ossman38a6be42009-03-25 12:26:41 +0000262 while (pixels--) {
263 *(dst++) = *r;
264 *(dst++) = *g;
265 *(dst++) = *b;
266 r += 4;
267 g += 4;
268 b += 4;
269 }
270 } else {
271 // Generic code
272 Pixel p;
273 rdr::U8 r, g, b;
274
275 while (pixels--) {
276 p = pixelFromBuffer(src);
277 src += bpp/8;
278
279 rgbFromPixel(p, cm, &r, &g, &b);
280 *(dst++) = r;
281 *(dst++) = g;
282 *(dst++) = b;
283 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000284 }
285}
286
287
DRCffe09d62011-08-17 02:27:59 +0000288void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
289 int w, int pitch, int h, ColourMap* cm) const
290{
DRCffe09d62011-08-17 02:27:59 +0000291 if (is888()) {
292 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100293 const rdr::U8 *r, *g, *b;
DRCffe09d62011-08-17 02:27:59 +0000294
295 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100296 r = src + (24 - redShift)/8;
297 g = src + (24 - greenShift)/8;
298 b = src + (24 - blueShift)/8;
DRCffe09d62011-08-17 02:27:59 +0000299 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100300 r = src + redShift/8;
301 g = src + greenShift/8;
302 b = src + blueShift/8;
DRCffe09d62011-08-17 02:27:59 +0000303 }
304
DRCbf79f682011-08-19 16:08:09 +0000305 int srcPad = pitch - w * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100306 while (h--) {
307 int w_ = w;
308 while (w_--) {
309 *(dst++) = *r;
310 *(dst++) = *g;
311 *(dst++) = *b;
312 r += 4;
313 g += 4;
314 b += 4;
DRCffe09d62011-08-17 02:27:59 +0000315 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100316 r += srcPad;
317 g += srcPad;
318 b += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000319 }
320 } else {
321 // Generic code
Pierre Ossman2baf7022014-01-20 16:40:10 +0100322 int srcPad = pitch - w * bpp/8;
323 while (h--) {
324 int w_ = w;
325 while (w_--) {
326 Pixel p;
327 rdr::U8 r, g, b;
DRCffe09d62011-08-17 02:27:59 +0000328
DRCbf79f682011-08-19 16:08:09 +0000329 p = pixelFromBuffer(src);
DRCffe09d62011-08-17 02:27:59 +0000330
331 rgbFromPixel(p, cm, &r, &g, &b);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100332
DRCffe09d62011-08-17 02:27:59 +0000333 *(dst++) = r;
334 *(dst++) = g;
335 *(dst++) = b;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100336 src += bpp/8;
DRCffe09d62011-08-17 02:27:59 +0000337 }
DRCbf79f682011-08-19 16:08:09 +0000338 src += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000339 }
340 }
341}
342
343
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000344void PixelFormat::print(char* str, int len) const
345{
346 // Unfortunately snprintf is not widely available so we build the string up
347 // using strncat - not pretty, but should be safe against buffer overruns.
348
349 char num[20];
350 if (len < 1) return;
351 str[0] = 0;
352 strncat(str, "depth ", len-1-strlen(str));
353 sprintf(num,"%d",depth);
354 strncat(str, num, len-1-strlen(str));
355 strncat(str, " (", len-1-strlen(str));
356 sprintf(num,"%d",bpp);
357 strncat(str, num, len-1-strlen(str));
358 strncat(str, "bpp)", len-1-strlen(str));
359 if (bpp != 8) {
360 if (bigEndian)
361 strncat(str, " big-endian", len-1-strlen(str));
362 else
363 strncat(str, " little-endian", len-1-strlen(str));
364 }
365
366 if (!trueColour) {
367 strncat(str, " color-map", len-1-strlen(str));
368 return;
369 }
370
371 if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
372 blueMax == (1 << greenShift) - 1 &&
373 greenMax == (1 << (redShift-greenShift)) - 1 &&
374 redMax == (1 << (depth-redShift)) - 1)
375 {
376 strncat(str, " rgb", len-1-strlen(str));
377 sprintf(num,"%d",depth-redShift);
378 strncat(str, num, len-1-strlen(str));
379 sprintf(num,"%d",redShift-greenShift);
380 strncat(str, num, len-1-strlen(str));
381 sprintf(num,"%d",greenShift);
382 strncat(str, num, len-1-strlen(str));
383 return;
384 }
385
386 if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
387 redMax == (1 << greenShift) - 1 &&
388 greenMax == (1 << (blueShift-greenShift)) - 1 &&
389 blueMax == (1 << (depth-blueShift)) - 1)
390 {
391 strncat(str, " bgr", len-1-strlen(str));
392 sprintf(num,"%d",depth-blueShift);
393 strncat(str, num, len-1-strlen(str));
394 sprintf(num,"%d",blueShift-greenShift);
395 strncat(str, num, len-1-strlen(str));
396 sprintf(num,"%d",greenShift);
397 strncat(str, num, len-1-strlen(str));
398 return;
399 }
400
401 strncat(str, " rgb max ", len-1-strlen(str));
402 sprintf(num,"%d,",redMax);
403 strncat(str, num, len-1-strlen(str));
404 sprintf(num,"%d,",greenMax);
405 strncat(str, num, len-1-strlen(str));
406 sprintf(num,"%d",blueMax);
407 strncat(str, num, len-1-strlen(str));
408 strncat(str, " shift ", len-1-strlen(str));
409 sprintf(num,"%d,",redShift);
410 strncat(str, num, len-1-strlen(str));
411 sprintf(num,"%d,",greenShift);
412 strncat(str, num, len-1-strlen(str));
413 sprintf(num,"%d",blueShift);
414 strncat(str, num, len-1-strlen(str));
415}
416
417
418bool PixelFormat::parse(const char* str)
419{
420 char rgbbgr[4];
421 int bits1, bits2, bits3;
422 if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
423 return false;
424
425 depth = bits1 + bits2 + bits3;
426 bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
427 trueColour = true;
428 rdr::U32 endianTest = 1;
429 bigEndian = (*(rdr::U8*)&endianTest == 0);
430
431 greenShift = bits3;
432 greenMax = (1 << bits2) - 1;
433
434 if (strcasecmp(rgbbgr, "bgr") == 0) {
435 redShift = 0;
436 redMax = (1 << bits3) - 1;
437 blueShift = bits3 + bits2;
438 blueMax = (1 << bits1) - 1;
439 } else if (strcasecmp(rgbbgr, "rgb") == 0) {
440 blueShift = 0;
441 blueMax = (1 << bits3) - 1;
442 redShift = bits3 + bits2;
443 redMax = (1 << bits1) - 1;
444 } else {
445 return false;
446 }
Pierre Ossman430db3d2009-04-03 12:49:38 +0000447
Pierre Ossman6655d962014-01-20 14:50:19 +0100448 assert(isSane());
449
Pierre Ossman19dbca22009-04-21 17:30:45 +0000450 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000451
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000452 return true;
453}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000454
455
456static int bits(rdr::U16 value)
457{
458 int bits;
459
460 bits = 16;
461
462 if (!(value & 0xff00)) {
463 bits -= 8;
464 value <<= 8;
465 }
466 if (!(value & 0xf000)) {
467 bits -= 4;
468 value <<= 4;
469 }
470 if (!(value & 0xc000)) {
471 bits -= 2;
472 value <<= 2;
473 }
474 if (!(value & 0x8000)) {
475 bits -= 1;
476 value <<= 1;
477 }
478
479 return bits;
480}
481
Pierre Ossman19dbca22009-04-21 17:30:45 +0000482void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000483{
Pierre Ossman19dbca22009-04-21 17:30:45 +0000484 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000485
486 redBits = bits(redMax);
487 greenBits = bits(greenMax);
488 blueBits = bits(blueMax);
489
Pierre Ossman6e5cd5d2014-02-28 11:54:34 +0100490 maxBits = redBits;
491 if (greenBits > maxBits)
492 maxBits = greenBits;
493 if (blueBits > maxBits)
494 maxBits = blueBits;
495
496 minBits = redBits;
497 if (greenBits < minBits)
498 minBits = greenBits;
499 if (blueBits < minBits)
500 minBits = blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000501
502 if (((*(char*)&endianTest) == 0) != bigEndian)
503 endianMismatch = true;
504 else
505 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000506}
Pierre Ossman6655d962014-01-20 14:50:19 +0100507
508bool PixelFormat::isSane(void)
509{
510 int totalBits;
511
512 if ((bpp != 8) && (bpp != 16) && (bpp != 32))
513 return false;
514 if (depth > bpp)
515 return false;
516
517 if (!trueColour && (depth != 8))
518 return false;
519
520 if (trueColour) {
521 if ((redMax & (redMax + 1)) != 0)
522 return false;
523 if ((greenMax & (greenMax + 1)) != 0)
524 return false;
525 if ((blueMax & (blueMax + 1)) != 0)
526 return false;
527
Pierre Ossman8b874e42014-01-20 17:23:51 +0100528 /*
529 * We don't allow individual channels > 8 bits in order to keep our
530 * conversions simple.
531 */
532 if (redMax >= (1 << 8))
533 return false;
534 if (greenMax >= (1 << 8))
535 return false;
536 if (blueMax >= (1 << 8))
537 return false;
538
Pierre Ossman6655d962014-01-20 14:50:19 +0100539 totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
540 if (totalBits > bpp)
541 return false;
542
543 if (((redMax << redShift) & (greenMax << greenShift)) != 0)
544 return false;
545 if (((redMax << redShift) & (blueMax << blueShift)) != 0)
546 return false;
547 if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
548 return false;
549 }
550
551 return true;
552}