blob: c70a170187b1f7405255d6ab8442b9b3d0a7e567 [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
Pierre Ossman8432ec12014-01-20 17:11:19 +0100140 rdr::U8 *r, *g, *b, *x;
Pierre Ossman19501b82009-03-31 14:06:53 +0000141
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;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100146 x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
Pierre Ossmanf61db642010-10-13 10:45:21 +0000147 } else {
148 r = dst + redShift/8;
149 g = dst + greenShift/8;
150 b = dst + blueShift/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100151 x = dst + (48 - redShift - greenShift - blueShift)/8;
Pierre Ossmanf61db642010-10-13 10:45:21 +0000152 }
Pierre Ossman19501b82009-03-31 14:06:53 +0000153
154 while (pixels--) {
155 *r = *(src++);
156 *g = *(src++);
157 *b = *(src++);
Pierre Ossman8432ec12014-01-20 17:11:19 +0100158 *x = 0;
Pierre Ossman19501b82009-03-31 14:06:53 +0000159 r += 4;
160 g += 4;
161 b += 4;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100162 x += 4;
Pierre Ossman19501b82009-03-31 14:06:53 +0000163 }
164 } else {
165 // Generic code
166 Pixel p;
167 rdr::U8 r, g, b;
168
169 while (pixels--) {
170 r = *(src++);
171 g = *(src++);
172 b = *(src++);
173
174 p = pixelFromRGB(r, g, b, cm);
175
176 bufferFromPixel(dst, p);
177 dst += bpp/8;
178 }
179 }
180}
181
DRC33c15e32011-11-03 18:49:21 +0000182void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
183 int w, int pitch, int h, ColourMap* cm) const
184{
185 if (is888()) {
186 // Optimised common case
Pierre Ossman8432ec12014-01-20 17:11:19 +0100187 rdr::U8 *r, *g, *b, *x;
DRC33c15e32011-11-03 18:49:21 +0000188
189 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100190 r = dst + (24 - redShift)/8;
191 g = dst + (24 - greenShift)/8;
192 b = dst + (24 - blueShift)/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100193 x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
DRC33c15e32011-11-03 18:49:21 +0000194 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100195 r = dst + redShift/8;
196 g = dst + greenShift/8;
197 b = dst + blueShift/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100198 x = dst + (48 - redShift - greenShift - blueShift)/8;
DRC33c15e32011-11-03 18:49:21 +0000199 }
200
201 int dstPad = pitch - w * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100202 while (h--) {
203 int w_ = w;
204 while (w_--) {
205 *r = *(src++);
206 *g = *(src++);
207 *b = *(src++);
Pierre Ossman8432ec12014-01-20 17:11:19 +0100208 *x = 0;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100209 r += 4;
210 g += 4;
211 b += 4;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100212 x += 4;
DRC33c15e32011-11-03 18:49:21 +0000213 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100214 r += dstPad;
215 g += dstPad;
216 b += dstPad;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100217 x += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000218 }
219 } else {
220 // Generic code
Pierre Ossman2baf7022014-01-20 16:40:10 +0100221 int dstPad = pitch - w * 4;
222 while (h--) {
223 int w_ = w;
224 while (w_--) {
225 Pixel p;
226 rdr::U8 r, g, b;
DRC33c15e32011-11-03 18:49:21 +0000227
DRC33c15e32011-11-03 18:49:21 +0000228 r = *(src++);
229 g = *(src++);
230 b = *(src++);
231
232 p = pixelFromRGB(r, g, b, cm);
233
234 bufferFromPixel(dst, p);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100235 dst += bpp/8;
DRC33c15e32011-11-03 18:49:21 +0000236 }
237 dst += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000238 }
239 }
240}
241
242
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000243void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
244{
245 rdr::U16 r, g, b;
246
247 rgbFromPixel(p, cm, &r, &g, &b);
248
249 rgb->r = r;
250 rgb->g = g;
251 rgb->b = b;
252}
253
254
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000255void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
256{
Pierre Ossman38a6be42009-03-25 12:26:41 +0000257 if (is888()) {
258 // Optimised common case
259 const rdr::U8 *r, *g, *b;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000260
Pierre Ossmanf61db642010-10-13 10:45:21 +0000261 if (bigEndian) {
262 r = src + (24 - redShift)/8;
263 g = src + (24 - greenShift)/8;
264 b = src + (24 - blueShift)/8;
265 } else {
266 r = src + redShift/8;
267 g = src + greenShift/8;
268 b = src + blueShift/8;
269 }
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000270
Pierre Ossman38a6be42009-03-25 12:26:41 +0000271 while (pixels--) {
272 *(dst++) = *r;
273 *(dst++) = *g;
274 *(dst++) = *b;
275 r += 4;
276 g += 4;
277 b += 4;
278 }
279 } else {
280 // Generic code
281 Pixel p;
282 rdr::U8 r, g, b;
283
284 while (pixels--) {
285 p = pixelFromBuffer(src);
286 src += bpp/8;
287
288 rgbFromPixel(p, cm, &r, &g, &b);
289 *(dst++) = r;
290 *(dst++) = g;
291 *(dst++) = b;
292 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293 }
294}
295
296
DRCffe09d62011-08-17 02:27:59 +0000297void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
298 int w, int pitch, int h, ColourMap* cm) const
299{
DRCffe09d62011-08-17 02:27:59 +0000300 if (is888()) {
301 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100302 const rdr::U8 *r, *g, *b;
DRCffe09d62011-08-17 02:27:59 +0000303
304 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100305 r = src + (24 - redShift)/8;
306 g = src + (24 - greenShift)/8;
307 b = src + (24 - blueShift)/8;
DRCffe09d62011-08-17 02:27:59 +0000308 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100309 r = src + redShift/8;
310 g = src + greenShift/8;
311 b = src + blueShift/8;
DRCffe09d62011-08-17 02:27:59 +0000312 }
313
DRCbf79f682011-08-19 16:08:09 +0000314 int srcPad = pitch - w * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100315 while (h--) {
316 int w_ = w;
317 while (w_--) {
318 *(dst++) = *r;
319 *(dst++) = *g;
320 *(dst++) = *b;
321 r += 4;
322 g += 4;
323 b += 4;
DRCffe09d62011-08-17 02:27:59 +0000324 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100325 r += srcPad;
326 g += srcPad;
327 b += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000328 }
329 } else {
330 // Generic code
Pierre Ossman2baf7022014-01-20 16:40:10 +0100331 int srcPad = pitch - w * bpp/8;
332 while (h--) {
333 int w_ = w;
334 while (w_--) {
335 Pixel p;
336 rdr::U8 r, g, b;
DRCffe09d62011-08-17 02:27:59 +0000337
DRCbf79f682011-08-19 16:08:09 +0000338 p = pixelFromBuffer(src);
DRCffe09d62011-08-17 02:27:59 +0000339
340 rgbFromPixel(p, cm, &r, &g, &b);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100341
DRCffe09d62011-08-17 02:27:59 +0000342 *(dst++) = r;
343 *(dst++) = g;
344 *(dst++) = b;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100345 src += bpp/8;
DRCffe09d62011-08-17 02:27:59 +0000346 }
DRCbf79f682011-08-19 16:08:09 +0000347 src += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000348 }
349 }
350}
351
352
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000353void PixelFormat::print(char* str, int len) const
354{
355 // Unfortunately snprintf is not widely available so we build the string up
356 // using strncat - not pretty, but should be safe against buffer overruns.
357
358 char num[20];
359 if (len < 1) return;
360 str[0] = 0;
361 strncat(str, "depth ", len-1-strlen(str));
362 sprintf(num,"%d",depth);
363 strncat(str, num, len-1-strlen(str));
364 strncat(str, " (", len-1-strlen(str));
365 sprintf(num,"%d",bpp);
366 strncat(str, num, len-1-strlen(str));
367 strncat(str, "bpp)", len-1-strlen(str));
368 if (bpp != 8) {
369 if (bigEndian)
370 strncat(str, " big-endian", len-1-strlen(str));
371 else
372 strncat(str, " little-endian", len-1-strlen(str));
373 }
374
375 if (!trueColour) {
376 strncat(str, " color-map", len-1-strlen(str));
377 return;
378 }
379
380 if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
381 blueMax == (1 << greenShift) - 1 &&
382 greenMax == (1 << (redShift-greenShift)) - 1 &&
383 redMax == (1 << (depth-redShift)) - 1)
384 {
385 strncat(str, " rgb", len-1-strlen(str));
386 sprintf(num,"%d",depth-redShift);
387 strncat(str, num, len-1-strlen(str));
388 sprintf(num,"%d",redShift-greenShift);
389 strncat(str, num, len-1-strlen(str));
390 sprintf(num,"%d",greenShift);
391 strncat(str, num, len-1-strlen(str));
392 return;
393 }
394
395 if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
396 redMax == (1 << greenShift) - 1 &&
397 greenMax == (1 << (blueShift-greenShift)) - 1 &&
398 blueMax == (1 << (depth-blueShift)) - 1)
399 {
400 strncat(str, " bgr", len-1-strlen(str));
401 sprintf(num,"%d",depth-blueShift);
402 strncat(str, num, len-1-strlen(str));
403 sprintf(num,"%d",blueShift-greenShift);
404 strncat(str, num, len-1-strlen(str));
405 sprintf(num,"%d",greenShift);
406 strncat(str, num, len-1-strlen(str));
407 return;
408 }
409
410 strncat(str, " rgb max ", len-1-strlen(str));
411 sprintf(num,"%d,",redMax);
412 strncat(str, num, len-1-strlen(str));
413 sprintf(num,"%d,",greenMax);
414 strncat(str, num, len-1-strlen(str));
415 sprintf(num,"%d",blueMax);
416 strncat(str, num, len-1-strlen(str));
417 strncat(str, " shift ", len-1-strlen(str));
418 sprintf(num,"%d,",redShift);
419 strncat(str, num, len-1-strlen(str));
420 sprintf(num,"%d,",greenShift);
421 strncat(str, num, len-1-strlen(str));
422 sprintf(num,"%d",blueShift);
423 strncat(str, num, len-1-strlen(str));
424}
425
426
427bool PixelFormat::parse(const char* str)
428{
429 char rgbbgr[4];
430 int bits1, bits2, bits3;
431 if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
432 return false;
433
434 depth = bits1 + bits2 + bits3;
435 bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
436 trueColour = true;
437 rdr::U32 endianTest = 1;
438 bigEndian = (*(rdr::U8*)&endianTest == 0);
439
440 greenShift = bits3;
441 greenMax = (1 << bits2) - 1;
442
443 if (strcasecmp(rgbbgr, "bgr") == 0) {
444 redShift = 0;
445 redMax = (1 << bits3) - 1;
446 blueShift = bits3 + bits2;
447 blueMax = (1 << bits1) - 1;
448 } else if (strcasecmp(rgbbgr, "rgb") == 0) {
449 blueShift = 0;
450 blueMax = (1 << bits3) - 1;
451 redShift = bits3 + bits2;
452 redMax = (1 << bits1) - 1;
453 } else {
454 return false;
455 }
Pierre Ossman430db3d2009-04-03 12:49:38 +0000456
Pierre Ossman6655d962014-01-20 14:50:19 +0100457 assert(isSane());
458
Pierre Ossman19dbca22009-04-21 17:30:45 +0000459 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000460
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000461 return true;
462}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000463
464
465static int bits(rdr::U16 value)
466{
467 int bits;
468
469 bits = 16;
470
471 if (!(value & 0xff00)) {
472 bits -= 8;
473 value <<= 8;
474 }
475 if (!(value & 0xf000)) {
476 bits -= 4;
477 value <<= 4;
478 }
479 if (!(value & 0xc000)) {
480 bits -= 2;
481 value <<= 2;
482 }
483 if (!(value & 0x8000)) {
484 bits -= 1;
485 value <<= 1;
486 }
487
488 return bits;
489}
490
Pierre Ossman19dbca22009-04-21 17:30:45 +0000491void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000492{
Pierre Ossman19dbca22009-04-21 17:30:45 +0000493 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000494
495 redBits = bits(redMax);
496 greenBits = bits(greenMax);
497 blueBits = bits(blueMax);
498
Pierre Ossman6e5cd5d2014-02-28 11:54:34 +0100499 maxBits = redBits;
500 if (greenBits > maxBits)
501 maxBits = greenBits;
502 if (blueBits > maxBits)
503 maxBits = blueBits;
504
505 minBits = redBits;
506 if (greenBits < minBits)
507 minBits = greenBits;
508 if (blueBits < minBits)
509 minBits = blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000510
511 if (((*(char*)&endianTest) == 0) != bigEndian)
512 endianMismatch = true;
513 else
514 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000515}
Pierre Ossman6655d962014-01-20 14:50:19 +0100516
517bool PixelFormat::isSane(void)
518{
519 int totalBits;
520
521 if ((bpp != 8) && (bpp != 16) && (bpp != 32))
522 return false;
523 if (depth > bpp)
524 return false;
525
526 if (!trueColour && (depth != 8))
527 return false;
528
529 if (trueColour) {
530 if ((redMax & (redMax + 1)) != 0)
531 return false;
532 if ((greenMax & (greenMax + 1)) != 0)
533 return false;
534 if ((blueMax & (blueMax + 1)) != 0)
535 return false;
536
Pierre Ossman8b874e42014-01-20 17:23:51 +0100537 /*
538 * We don't allow individual channels > 8 bits in order to keep our
539 * conversions simple.
540 */
541 if (redMax >= (1 << 8))
542 return false;
543 if (greenMax >= (1 << 8))
544 return false;
545 if (blueMax >= (1 << 8))
546 return false;
547
Pierre Ossman6655d962014-01-20 14:50:19 +0100548 totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
549 if (totalBits > bpp)
550 return false;
551
552 if (((redMax << redShift) & (greenMax << greenShift)) != 0)
553 return false;
554 if (((redMax << redShift) & (blueMax << blueShift)) != 0)
555 return false;
556 if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
557 return false;
558 }
559
560 return true;
561}