blob: 013cceb2c4ae53211aacbaf245b388183f2ea1d6 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman67b2b2f2009-03-06 10:12:55 +00002 * Copyright 2009 Pierre Ossman for Cendio AB
DRCffe09d62011-08-17 02:27:59 +00003 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
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>
25#include <rfb/PixelFormat.h>
26#include <rfb/util.h>
27
28#ifdef _WIN32
29#define strcasecmp _stricmp
30#endif
31
32using namespace rfb;
33
34PixelFormat::PixelFormat(int b, int d, bool e, bool t,
35 int rm, int gm, int bm, int rs, int gs, int bs)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000036 : bpp(b), depth(d), trueColour(t), bigEndian(e),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000037 redMax(rm), greenMax(gm), blueMax(bm),
38 redShift(rs), greenShift(gs), blueShift(bs)
39{
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000040 assert((bpp == 8) || (bpp == 16) || (bpp == 32));
41 assert(depth <= bpp);
42 assert((redMax & (redMax + 1)) == 0);
43 assert((greenMax & (greenMax + 1)) == 0);
44 assert((blueMax & (blueMax + 1)) == 0);
45
Pierre Ossman19dbca22009-04-21 17:30:45 +000046 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047}
48
49PixelFormat::PixelFormat()
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000050 : bpp(8), depth(8), trueColour(true), bigEndian(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051 redMax(7), greenMax(7), blueMax(3),
52 redShift(0), greenShift(3), blueShift(6)
53{
Pierre Ossman19dbca22009-04-21 17:30:45 +000054 updateState();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055}
56
57bool PixelFormat::equal(const PixelFormat& other) const
58{
59 return (bpp == other.bpp &&
60 depth == other.depth &&
61 (bigEndian == other.bigEndian || bpp == 8) &&
62 trueColour == other.trueColour &&
63 (!trueColour || (redMax == other.redMax &&
64 greenMax == other.greenMax &&
65 blueMax == other.blueMax &&
66 redShift == other.redShift &&
67 greenShift == other.greenShift &&
68 blueShift == other.blueShift)));
69}
70
71void PixelFormat::read(rdr::InStream* is)
72{
73 bpp = is->readU8();
74 depth = is->readU8();
75 bigEndian = is->readU8();
76 trueColour = is->readU8();
77 redMax = is->readU16();
78 greenMax = is->readU16();
79 blueMax = is->readU16();
80 redShift = is->readU8();
81 greenShift = is->readU8();
82 blueShift = is->readU8();
83 is->skip(3);
Pierre Ossman67b2b2f2009-03-06 10:12:55 +000084
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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000135Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue,
136 ColourMap* cm) const
137{
138 if (trueColour) {
139 rdr::U32 r = ((rdr::U32)red * redMax + 32767) / 65535;
140 rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535;
141 rdr::U32 b = ((rdr::U32)blue * blueMax + 32767) / 65535;
142
143 return (r << redShift) | (g << greenShift) | (b << blueShift);
144 } else if (cm) {
145 // Try to find the closest pixel by Cartesian distance
146 int colours = 1 << depth;
147 int diff = 256 * 256 * 4;
148 int col = 0;
149 for (int i=0; i<colours; i++) {
150 int r, g, b;
151 cm->lookup(i, &r, &g, &b);
152 int rd = (r-red) >> 8;
153 int gd = (g-green) >> 8;
154 int bd = (b-blue) >> 8;
155 int d = rd*rd + gd*gd + bd*bd;
156 if (d < diff) {
157 col = i;
158 diff = d;
159 }
160 }
161 return col;
162 }
163 // XXX just return 0 for colour map?
164 return 0;
165}
166
167
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000168Pixel PixelFormat::pixelFromRGB(rdr::U8 red, rdr::U8 green, rdr::U8 blue,
169 ColourMap* cm) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170{
171 if (trueColour) {
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000172 rdr::U32 r = ((rdr::U32)red * redMax + 127) / 255;
173 rdr::U32 g = ((rdr::U32)green * greenMax + 127) / 255;
174 rdr::U32 b = ((rdr::U32)blue * blueMax + 127) / 255;
175
176 return (r << redShift) | (g << greenShift) | (b << blueShift);
177 }
178
179 return pixelFromRGB((rdr::U16)(red << 8), (rdr::U16)(green << 8),
180 (rdr::U16)(blue << 8), cm);
181}
182
183
Pierre Ossman19501b82009-03-31 14:06:53 +0000184void PixelFormat::bufferFromRGB(rdr::U8 *dst, const rdr::U8* src,
185 int pixels, ColourMap* cm) const
186{
187 if (is888()) {
188 // Optimised common case
189 rdr::U8 *r, *g, *b;
190
Pierre Ossmanf61db642010-10-13 10:45:21 +0000191 if (bigEndian) {
192 r = dst + (24 - redShift)/8;
193 g = dst + (24 - greenShift)/8;
194 b = dst + (24 - blueShift)/8;
195 } else {
196 r = dst + redShift/8;
197 g = dst + greenShift/8;
198 b = dst + blueShift/8;
199 }
Pierre Ossman19501b82009-03-31 14:06:53 +0000200
201 while (pixels--) {
202 *r = *(src++);
203 *g = *(src++);
204 *b = *(src++);
205 r += 4;
206 g += 4;
207 b += 4;
208 }
209 } else {
210 // Generic code
211 Pixel p;
212 rdr::U8 r, g, b;
213
214 while (pixels--) {
215 r = *(src++);
216 g = *(src++);
217 b = *(src++);
218
219 p = pixelFromRGB(r, g, b, cm);
220
221 bufferFromPixel(dst, p);
222 dst += bpp/8;
223 }
224 }
225}
226
227
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000228void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
229{
230 rdr::U16 r, g, b;
231
232 rgbFromPixel(p, cm, &r, &g, &b);
233
234 rgb->r = r;
235 rgb->g = g;
236 rgb->b = b;
237}
238
239
240void PixelFormat::rgbFromBuffer(rdr::U16* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
241{
242 Pixel p;
243 rdr::U16 r, g, b;
244
245 while (pixels--) {
246 p = pixelFromBuffer(src);
247 src += bpp/8;
248
249 rgbFromPixel(p, cm, &r, &g, &b);
250 *(dst++) = r;
251 *(dst++) = g;
252 *(dst++) = b;
253 }
254}
255
256
257void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm) const
258{
Pierre Ossman38a6be42009-03-25 12:26:41 +0000259 if (is888()) {
260 // Optimised common case
261 const rdr::U8 *r, *g, *b;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000262
Pierre Ossmanf61db642010-10-13 10:45:21 +0000263 if (bigEndian) {
264 r = src + (24 - redShift)/8;
265 g = src + (24 - greenShift)/8;
266 b = src + (24 - blueShift)/8;
267 } else {
268 r = src + redShift/8;
269 g = src + greenShift/8;
270 b = src + blueShift/8;
271 }
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000272
Pierre Ossman38a6be42009-03-25 12:26:41 +0000273 while (pixels--) {
274 *(dst++) = *r;
275 *(dst++) = *g;
276 *(dst++) = *b;
277 r += 4;
278 g += 4;
279 b += 4;
280 }
281 } else {
282 // Generic code
283 Pixel p;
284 rdr::U8 r, g, b;
285
286 while (pixels--) {
287 p = pixelFromBuffer(src);
288 src += bpp/8;
289
290 rgbFromPixel(p, cm, &r, &g, &b);
291 *(dst++) = r;
292 *(dst++) = g;
293 *(dst++) = b;
294 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000295 }
296}
297
298
DRCffe09d62011-08-17 02:27:59 +0000299void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
300 int w, int pitch, int h, ColourMap* cm) const
301{
302 rdr::U8 *rowptr, *colptr;
303
304 if (is888()) {
305 // Optimised common case
306 int rindex, gindex, bindex;
307
308 if (bigEndian) {
309 rindex = (24 - redShift)/8;
310 gindex = (24 - greenShift)/8;
311 bindex = (24 - blueShift)/8;
312 } else {
313 rindex = redShift/8;
314 gindex = greenShift/8;
315 bindex = blueShift/8;
316 }
317
318 for(rowptr = (rdr::U8 *)src; rowptr < &src[pitch * h]; rowptr += pitch) {
319 for(colptr = rowptr; colptr < &rowptr[w * 4]; colptr += 4) {
320 *(dst++) = colptr[rindex];
321 *(dst++) = colptr[gindex];
322 *(dst++) = colptr[bindex];
323 }
324 }
325 } else {
326 // Generic code
327 Pixel p;
328 rdr::U8 r, g, b;
329
330 for(rowptr = (rdr::U8 *)src; rowptr < &src[pitch * h]; rowptr += pitch) {
331 for(colptr = rowptr; colptr < &rowptr[w * bpp/8]; colptr += bpp/8) {
332 p = pixelFromBuffer(colptr);
333
334 rgbFromPixel(p, cm, &r, &g, &b);
335 *(dst++) = r;
336 *(dst++) = g;
337 *(dst++) = b;
338 }
339 }
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 Ossman19dbca22009-04-21 17:30:45 +0000448 updateState();
Pierre Ossman430db3d2009-04-03 12:49:38 +0000449
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000450 return true;
451}
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000452
453
454static int bits(rdr::U16 value)
455{
456 int bits;
457
458 bits = 16;
459
460 if (!(value & 0xff00)) {
461 bits -= 8;
462 value <<= 8;
463 }
464 if (!(value & 0xf000)) {
465 bits -= 4;
466 value <<= 4;
467 }
468 if (!(value & 0xc000)) {
469 bits -= 2;
470 value <<= 2;
471 }
472 if (!(value & 0x8000)) {
473 bits -= 1;
474 value <<= 1;
475 }
476
477 return bits;
478}
479
Pierre Ossman19dbca22009-04-21 17:30:45 +0000480void PixelFormat::updateState(void)
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000481{
482 int redBits, greenBits, blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000483 int endianTest = 1;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000484
485 redBits = bits(redMax);
486 greenBits = bits(greenMax);
487 blueBits = bits(blueMax);
488
489 redConvShift = 16 - redBits;
490 greenConvShift = 16 - greenBits;
491 blueConvShift = 16 - blueBits;
Pierre Ossman19dbca22009-04-21 17:30:45 +0000492
493 if (((*(char*)&endianTest) == 0) != bigEndian)
494 endianMismatch = true;
495 else
496 endianMismatch = false;
Pierre Ossman67b2b2f2009-03-06 10:12:55 +0000497}