blob: b11f8836f020aaa61131a1b2bc5f21a65f56dd28 [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{
Pierre Ossmanb2665612014-01-30 09:51:39 +0100138 bufferFromRGB(dst, src, pixels, pixels, 1, cm);
Pierre Ossman19501b82009-03-31 14:06:53 +0000139}
140
DRC33c15e32011-11-03 18:49:21 +0000141void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100142 int w, int stride, int h, ColourMap* cm) const
DRC33c15e32011-11-03 18:49:21 +0000143{
144 if (is888()) {
145 // Optimised common case
Pierre Ossman8432ec12014-01-20 17:11:19 +0100146 rdr::U8 *r, *g, *b, *x;
DRC33c15e32011-11-03 18:49:21 +0000147
148 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100149 r = dst + (24 - redShift)/8;
150 g = dst + (24 - greenShift)/8;
151 b = dst + (24 - blueShift)/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100152 x = dst + (24 - (48 - redShift - greenShift - blueShift))/8;
DRC33c15e32011-11-03 18:49:21 +0000153 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100154 r = dst + redShift/8;
155 g = dst + greenShift/8;
156 b = dst + blueShift/8;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100157 x = dst + (48 - redShift - greenShift - blueShift)/8;
DRC33c15e32011-11-03 18:49:21 +0000158 }
159
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100160 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100161 while (h--) {
162 int w_ = w;
163 while (w_--) {
164 *r = *(src++);
165 *g = *(src++);
166 *b = *(src++);
Pierre Ossman8432ec12014-01-20 17:11:19 +0100167 *x = 0;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100168 r += 4;
169 g += 4;
170 b += 4;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100171 x += 4;
DRC33c15e32011-11-03 18:49:21 +0000172 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100173 r += dstPad;
174 g += dstPad;
175 b += dstPad;
Pierre Ossman8432ec12014-01-20 17:11:19 +0100176 x += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000177 }
178 } else {
179 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100180 int dstPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100181 while (h--) {
182 int w_ = w;
183 while (w_--) {
184 Pixel p;
185 rdr::U8 r, g, b;
DRC33c15e32011-11-03 18:49:21 +0000186
DRC33c15e32011-11-03 18:49:21 +0000187 r = *(src++);
188 g = *(src++);
189 b = *(src++);
190
191 p = pixelFromRGB(r, g, b, cm);
192
193 bufferFromPixel(dst, p);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100194 dst += bpp/8;
DRC33c15e32011-11-03 18:49:21 +0000195 }
196 dst += dstPad;
DRC33c15e32011-11-03 18:49:21 +0000197 }
198 }
199}
200
201
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000202void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
203{
204 rdr::U16 r, g, b;
205
206 rgbFromPixel(p, cm, &r, &g, &b);
207
208 rgb->r = r;
209 rgb->g = g;
210 rgb->b = b;
211}
212
213
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000214void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
215{
Pierre Ossmanb2665612014-01-30 09:51:39 +0100216 rgbFromBuffer(dst, src, pixels, pixels, 1, cm);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000217}
218
219
DRCffe09d62011-08-17 02:27:59 +0000220void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100221 int w, int stride, int h, ColourMap* cm) const
DRCffe09d62011-08-17 02:27:59 +0000222{
DRCffe09d62011-08-17 02:27:59 +0000223 if (is888()) {
224 // Optimised common case
Pierre Ossman2baf7022014-01-20 16:40:10 +0100225 const rdr::U8 *r, *g, *b;
DRCffe09d62011-08-17 02:27:59 +0000226
227 if (bigEndian) {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100228 r = src + (24 - redShift)/8;
229 g = src + (24 - greenShift)/8;
230 b = src + (24 - blueShift)/8;
DRCffe09d62011-08-17 02:27:59 +0000231 } else {
Pierre Ossman2baf7022014-01-20 16:40:10 +0100232 r = src + redShift/8;
233 g = src + greenShift/8;
234 b = src + blueShift/8;
DRCffe09d62011-08-17 02:27:59 +0000235 }
236
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100237 int srcPad = (stride - w) * 4;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100238 while (h--) {
239 int w_ = w;
240 while (w_--) {
241 *(dst++) = *r;
242 *(dst++) = *g;
243 *(dst++) = *b;
244 r += 4;
245 g += 4;
246 b += 4;
DRCffe09d62011-08-17 02:27:59 +0000247 }
Pierre Ossman2baf7022014-01-20 16:40:10 +0100248 r += srcPad;
249 g += srcPad;
250 b += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000251 }
252 } else {
253 // Generic code
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100254 int srcPad = (stride - w) * bpp/8;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100255 while (h--) {
256 int w_ = w;
257 while (w_--) {
258 Pixel p;
259 rdr::U8 r, g, b;
DRCffe09d62011-08-17 02:27:59 +0000260
DRCbf79f682011-08-19 16:08:09 +0000261 p = pixelFromBuffer(src);
DRCffe09d62011-08-17 02:27:59 +0000262
263 rgbFromPixel(p, cm, &r, &g, &b);
Pierre Ossman2baf7022014-01-20 16:40:10 +0100264
DRCffe09d62011-08-17 02:27:59 +0000265 *(dst++) = r;
266 *(dst++) = g;
267 *(dst++) = b;
Pierre Ossman2baf7022014-01-20 16:40:10 +0100268 src += bpp/8;
DRCffe09d62011-08-17 02:27:59 +0000269 }
DRCbf79f682011-08-19 16:08:09 +0000270 src += srcPad;
DRCffe09d62011-08-17 02:27:59 +0000271 }
272 }
273}
274
275
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000276void PixelFormat::print(char* str, int len) const
277{
278 // Unfortunately snprintf is not widely available so we build the string up
279 // using strncat - not pretty, but should be safe against buffer overruns.
280
281 char num[20];
282 if (len < 1) return;
283 str[0] = 0;
284 strncat(str, "depth ", len-1-strlen(str));
285 sprintf(num,"%d",depth);
286 strncat(str, num, len-1-strlen(str));
287 strncat(str, " (", len-1-strlen(str));
288 sprintf(num,"%d",bpp);
289 strncat(str, num, len-1-strlen(str));
290 strncat(str, "bpp)", len-1-strlen(str));
291 if (bpp != 8) {
292 if (bigEndian)
293 strncat(str, " big-endian", len-1-strlen(str));
294 else
295 strncat(str, " little-endian", len-1-strlen(str));
296 }
297
298 if (!trueColour) {
299 strncat(str, " color-map", len-1-strlen(str));
300 return;
301 }
302
303 if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
304 blueMax == (1 << greenShift) - 1 &&
305 greenMax == (1 << (redShift-greenShift)) - 1 &&
306 redMax == (1 << (depth-redShift)) - 1)
307 {
308 strncat(str, " rgb", len-1-strlen(str));
309 sprintf(num,"%d",depth-redShift);
310 strncat(str, num, len-1-strlen(str));
311 sprintf(num,"%d",redShift-greenShift);
312 strncat(str, num, len-1-strlen(str));
313 sprintf(num,"%d",greenShift);
314 strncat(str, num, len-1-strlen(str));
315 return;
316 }
317
318 if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
319 redMax == (1 << greenShift) - 1 &&
320 greenMax == (1 << (blueShift-greenShift)) - 1 &&
321 blueMax == (1 << (depth-blueShift)) - 1)
322 {
323 strncat(str, " bgr", len-1-strlen(str));
324 sprintf(num,"%d",depth-blueShift);
325 strncat(str, num, len-1-strlen(str));
326 sprintf(num,"%d",blueShift-greenShift);
327 strncat(str, num, len-1-strlen(str));
328 sprintf(num,"%d",greenShift);
329 strncat(str, num, len-1-strlen(str));
330 return;
331 }
332
333 strncat(str, " rgb max ", len-1-strlen(str));
334 sprintf(num,"%d,",redMax);
335 strncat(str, num, len-1-strlen(str));
336 sprintf(num,"%d,",greenMax);
337 strncat(str, num, len-1-strlen(str));
338 sprintf(num,"%d",blueMax);
339 strncat(str, num, len-1-strlen(str));
340 strncat(str, " shift ", len-1-strlen(str));
341 sprintf(num,"%d,",redShift);
342 strncat(str, num, len-1-strlen(str));
343 sprintf(num,"%d,",greenShift);
344 strncat(str, num, len-1-strlen(str));
345 sprintf(num,"%d",blueShift);
346 strncat(str, num, len-1-strlen(str));
347}
348
349
350bool PixelFormat::parse(const char* str)
351{
352 char rgbbgr[4];
353 int bits1, bits2, bits3;
354 if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
355 return false;
356
357 depth = bits1 + bits2 + bits3;
358 bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
359 trueColour = true;
360 rdr::U32 endianTest = 1;
361 bigEndian = (*(rdr::U8*)&endianTest == 0);
362
363 greenShift = bits3;
364 greenMax = (1 << bits2) - 1;
365
366 if (strcasecmp(rgbbgr, "bgr") == 0) {
367 redShift = 0;
368 redMax = (1 << bits3) - 1;
369 blueShift = bits3 + bits2;
370 blueMax = (1 << bits1) - 1;
371 } else if (strcasecmp(rgbbgr, "rgb") == 0) {
372 blueShift = 0;
373 blueMax = (1 << bits3) - 1;
374 redShift = bits3 + bits2;
375 redMax = (1 << bits1) - 1;
376 } else {
377 return false;
378 }
Pierre Ossman430db3d2009-04-03 12:49:38 +0000379
Pierre Ossman6655d962014-01-20 14:50:19 +0100380 assert(isSane());
381
Pierre Ossman19dbca22009-04-21 17:30:45 +0000382 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000383
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000384 return true;
385}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000386
387
388static int bits(rdr::U16 value)
389{
390 int bits;
391
392 bits = 16;
393
394 if (!(value & 0xff00)) {
395 bits -= 8;
396 value <<= 8;
397 }
398 if (!(value & 0xf000)) {
399 bits -= 4;
400 value <<= 4;
401 }
402 if (!(value & 0xc000)) {
403 bits -= 2;
404 value <<= 2;
405 }
406 if (!(value & 0x8000)) {
407 bits -= 1;
408 value <<= 1;
409 }
410
411 return bits;
412}
413
Pierre Ossman19dbca22009-04-21 17:30:45 +0000414void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000415{
Pierre Ossman19dbca22009-04-21 17:30:45 +0000416 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000417
418 redBits = bits(redMax);
419 greenBits = bits(greenMax);
420 blueBits = bits(blueMax);
421
Pierre Ossman6e5cd5d2014-02-28 11:54:34 +0100422 maxBits = redBits;
423 if (greenBits > maxBits)
424 maxBits = greenBits;
425 if (blueBits > maxBits)
426 maxBits = blueBits;
427
428 minBits = redBits;
429 if (greenBits < minBits)
430 minBits = greenBits;
431 if (blueBits < minBits)
432 minBits = blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000433
434 if (((*(char*)&endianTest) == 0) != bigEndian)
435 endianMismatch = true;
436 else
437 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000438}
Pierre Ossman6655d962014-01-20 14:50:19 +0100439
440bool PixelFormat::isSane(void)
441{
442 int totalBits;
443
444 if ((bpp != 8) && (bpp != 16) && (bpp != 32))
445 return false;
446 if (depth > bpp)
447 return false;
448
449 if (!trueColour && (depth != 8))
450 return false;
451
452 if (trueColour) {
453 if ((redMax & (redMax + 1)) != 0)
454 return false;
455 if ((greenMax & (greenMax + 1)) != 0)
456 return false;
457 if ((blueMax & (blueMax + 1)) != 0)
458 return false;
459
Pierre Ossman8b874e42014-01-20 17:23:51 +0100460 /*
461 * We don't allow individual channels > 8 bits in order to keep our
462 * conversions simple.
463 */
464 if (redMax >= (1 << 8))
465 return false;
466 if (greenMax >= (1 << 8))
467 return false;
468 if (blueMax >= (1 << 8))
469 return false;
470
Pierre Ossman6655d962014-01-20 14:50:19 +0100471 totalBits = bits(redMax) + bits(greenMax) + bits(blueMax);
472 if (totalBits > bpp)
473 return false;
474
475 if (((redMax << redShift) & (greenMax << greenShift)) != 0)
476 return false;
477 if (((redMax << redShift) & (blueMax << blueShift)) != 0)
478 return false;
479 if (((greenMax << greenShift) & (blueMax << blueShift)) != 0)
480 return false;
481 }
482
483 return true;
484}