2
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
4
* This is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This software is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this software; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21
* colour.c - functions to deal with colour - i.e. RFB pixel formats, X visuals
22
* and colormaps. Thanks to Grant McDorman for some of the ideas used here.
25
#include "demoviewer.h"
29
#define INVALID_PIXEL 0xffffffff
30
#define MAX_CMAP_SIZE 256
31
#define BGR233_SIZE 256
32
unsigned long BGR233ToPixel[BGR233_SIZE];
36
unsigned int visdepth, visbpp;
37
Bool allocColorFailed = False;
39
static int nBGR233ColoursAllocated;
41
static Bool GetPseudoColorVisualAndCmap(int depth);
42
static Bool GetTrueColorVisualAndCmap(int depth);
43
static int GetBPPForDepth(int depth);
44
static void SetupBGR233Map();
45
static void AllocateExactBGR233Colours();
46
static Bool AllocateBGR233Colour(int r, int g, int b);
50
* SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are
51
* equivalent to the RFB protocol's "pixel format"). Having decided on the
52
* best visual, it also creates a colormap if necessary, sets the appropriate
53
* resources on the toplevel widget, and sets up the myFormat structure to
54
* describe the pixel format in terms that the RFB server will be able to
57
* The algorithm for deciding which visual to use is as follows:
59
* If forceOwnCmap is true then we try to use a PseudoColor visual - we first
60
* see if there's one of the same depth as the RFB server, followed by an 8-bit
63
* If forceTrueColour is true then we try to use a TrueColor visual - if
64
* requestedDepth is set then it must be of that depth, otherwise any depth
67
* Otherwise, we use the X server's default visual and colormap. If this is
68
* TrueColor then we just ask the RFB server for this format. If the default
69
* isn't TrueColor, or if useBGR233 is true, then we ask the RFB server for
70
* BGR233 pixel format and use a lookup table to translate to the nearest
71
* colours provided by the X server.
77
if (appData.forceOwnCmap) {
78
if (!si.format.trueColour) {
79
if (GetPseudoColorVisualAndCmap(si.format.depth))
82
if (GetPseudoColorVisualAndCmap(8))
84
fprintf(stderr,"Couldn't find a matching PseudoColor visual.\n");
87
if (appData.forceTrueColour) {
88
if (GetTrueColorVisualAndCmap(appData.requestedDepth))
90
fprintf(stderr,"Couldn't find a matching TrueColor visual.\n");
93
/* just use default visual and colormap */
95
vis = DefaultVisual(dpy,DefaultScreen(dpy));
96
visdepth = DefaultDepth(dpy,DefaultScreen(dpy));
97
visbpp = GetBPPForDepth(visdepth);
98
cmap = DefaultColormap(dpy,DefaultScreen(dpy));
100
if (!appData.useBGR233 && (vis->class == TrueColor)) {
102
myFormat.bitsPerPixel = visbpp;
103
myFormat.depth = visdepth;
104
myFormat.trueColour = 1;
105
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
106
myFormat.redShift = ffs(vis->red_mask) - 1;
107
myFormat.greenShift = ffs(vis->green_mask) - 1;
108
myFormat.blueShift = ffs(vis->blue_mask) - 1;
109
myFormat.redMax = vis->red_mask >> myFormat.redShift;
110
myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
111
myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
114
"Using default colormap which is TrueColor. Pixel format:\n");
115
PrintPixelFormat(&myFormat);
119
appData.useBGR233 = True;
121
myFormat.bitsPerPixel = 8;
123
myFormat.trueColour = 1;
124
myFormat.bigEndian = 0;
126
myFormat.greenMax = 7;
127
myFormat.blueMax = 3;
128
myFormat.redShift = 0;
129
myFormat.greenShift = 3;
130
myFormat.blueShift = 6;
133
"Using default colormap and translating from BGR233. Pixel format:\n");
134
PrintPixelFormat(&myFormat);
141
* GetPseudoColorVisualAndCmap tries to find a PseudoColor visual of the given
142
* depth. If successful it sets vis, visdepth, cmap and myFormat, and also
143
* sets the appropriate resources on the toplevel widget.
147
GetPseudoColorVisualAndCmap(int depth)
153
tmpl.screen = DefaultScreen(dpy);
155
tmpl.class = PseudoColor;
156
tmpl.colormap_size = (1 << depth);
158
vinfo = XGetVisualInfo(dpy,
159
VisualScreenMask|VisualDepthMask|
160
VisualClassMask|VisualColormapSizeMask,
164
vis = vinfo[0].visual;
165
visdepth = vinfo[0].depth;
167
visbpp = GetBPPForDepth(visdepth);
168
myFormat.bitsPerPixel = visbpp;
169
myFormat.depth = visdepth;
170
myFormat.trueColour = 0;
171
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
172
myFormat.redMax = myFormat.greenMax = myFormat.blueMax = 0;
173
myFormat.redShift = myFormat.greenShift = myFormat.blueShift = 0;
175
cmap = XCreateColormap(dpy, DefaultRootWindow(dpy), vis, AllocAll);
177
XtVaSetValues(toplevel, XtNcolormap, cmap, XtNdepth, visdepth,
178
XtNvisual, vis, NULL);
180
if (appData.fullScreen) {
181
XInstallColormap(dpy, cmap);
184
fprintf(stderr,"Using PseudoColor visual, depth %d. Pixel format:\n",
186
PrintPixelFormat(&myFormat);
196
* GetTrueColorVisualAndCmap tries to find a TrueColor visual of the given
197
* depth. If successful it sets vis, visdepth, cmap and myFormat, and also
198
* sets the appropriate resources on the toplevel widget.
202
GetTrueColorVisualAndCmap(int depth)
207
int mask = VisualScreenMask|VisualClassMask;
209
tmpl.screen = DefaultScreen(dpy);
210
tmpl.class = TrueColor;
214
mask |= VisualDepthMask;
217
vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nvis);
220
vis = vinfo[0].visual;
221
visdepth = vinfo[0].depth;
223
visbpp = GetBPPForDepth(visdepth);
224
myFormat.bitsPerPixel = visbpp;
225
myFormat.depth = visdepth;
226
myFormat.trueColour = 1;
227
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
228
myFormat.redShift = ffs(vis->red_mask) - 1;
229
myFormat.greenShift = ffs(vis->green_mask) - 1;
230
myFormat.blueShift = ffs(vis->blue_mask) - 1;
231
myFormat.redMax = vis->red_mask >> myFormat.redShift;
232
myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
233
myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
235
cmap = XCreateColormap(dpy, DefaultRootWindow(dpy), vis, AllocNone);
237
XtVaSetValues(toplevel, XtNcolormap, cmap, XtNdepth, visdepth,
238
XtNvisual, vis, NULL);
240
if (appData.fullScreen) {
241
XInstallColormap(dpy, cmap);
244
fprintf(stderr,"Using TrueColor visual, depth %d. Pixel format:\n",
246
PrintPixelFormat(&myFormat);
256
* GetBPPForDepth looks through the "pixmap formats" to find the bits-per-pixel
257
* for the given depth.
261
GetBPPForDepth(int depth)
263
XPixmapFormatValues *format;
268
format = XListPixmapFormats(dpy, &nformats);
270
for (i = 0; i < nformats; i++) {
271
if (format[i].depth == depth)
276
fprintf(stderr,"no pixmap format for depth %d???\n", depth);
280
bpp = format[i].bits_per_pixel;
284
if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) {
285
fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp);
295
* SetupBGR233Map() sets up the BGR233ToPixel array.
297
* It calls AllocateExactBGR233Colours to allocate some exact BGR233 colours
298
* (limited by space in the colormap and/or by the value of the nColours
299
* resource). If the number allocated is less than BGR233_SIZE then it fills
300
* the rest in using the "nearest" colours available. How this is done depends
301
* on the value of the useSharedColours resource. If it's false, we use only
302
* colours from the exact BGR233 colours we've just allocated. If it's true,
303
* then we also use other clients' "shared" colours available in the colormap.
311
unsigned long nearestPixel = 0;
313
XColor cmapEntry[MAX_CMAP_SIZE];
314
Bool exactBGR233[MAX_CMAP_SIZE];
315
Bool shared[MAX_CMAP_SIZE];
316
Bool usedAsNearest[MAX_CMAP_SIZE];
320
appData.nColours = 256; /* ignore nColours setting for > 8-bit deep */
323
for (i = 0; i < BGR233_SIZE; i++) {
324
BGR233ToPixel[i] = INVALID_PIXEL;
327
AllocateExactBGR233Colours();
329
fprintf(stderr,"Got %d exact BGR233 colours out of %d\n",
330
nBGR233ColoursAllocated, appData.nColours);
332
if (nBGR233ColoursAllocated < BGR233_SIZE) {
334
if (visdepth > 8) { /* shouldn't get here */
335
fprintf(stderr,"Error: couldn't allocate BGR233 colours even though "
336
"depth is %d\n", visdepth);
340
cmapSize = (1 << visdepth);
342
for (i = 0; i < cmapSize; i++) {
343
cmapEntry[i].pixel = i;
344
exactBGR233[i] = False;
346
usedAsNearest[i] = False;
349
XQueryColors(dpy, cmap, cmapEntry, cmapSize);
351
/* mark all our exact BGR233 pixels */
353
for (i = 0; i < BGR233_SIZE; i++) {
354
if (BGR233ToPixel[i] != INVALID_PIXEL)
355
exactBGR233[BGR233ToPixel[i]] = True;
358
if (appData.useSharedColours) {
360
/* Try to find existing shared colours. This is harder than it sounds
361
because XQueryColors doesn't tell us whether colours are shared,
362
private or unallocated. What we do is go through the colormap and for
363
each pixel try to allocate exactly its RGB values. If this returns a
364
different pixel then it's definitely either a private or unallocated
365
pixel, so no use to us. If it returns us the same pixel again, then
366
it's likely that it's a shared colour - however, it is possible that
367
it was actually an unallocated pixel, which we've now allocated. We
368
minimise this possibility by going through the pixels in reverse order
369
- this helps becuse the X server allocates new pixels from the lowest
370
number up, so it should only be a problem for the lowest unallocated
373
for (i = cmapSize-1; i >= 0; i--) {
374
if (!exactBGR233[i] &&
375
XAllocColor(dpy, cmap, &cmapEntry[i])) {
377
if (cmapEntry[i].pixel == i) {
379
shared[i] = True; /* probably shared */
383
/* "i" is either unallocated or private. We have now unnecessarily
384
allocated cmapEntry[i].pixel. Free it. */
386
XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0);
392
/* Now fill in the nearest colours */
394
for (r = 0; r < 8; r++) {
395
for (g = 0; g < 8; g++) {
396
for (b = 0; b < 4; b++) {
397
if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) {
399
unsigned long minDistance = ULONG_MAX;
401
for (i = 0; i < cmapSize; i++) {
402
if (exactBGR233[i] || shared[i]) {
403
unsigned long distance
404
= (abs(cmapEntry[i].red - r * 65535 / 7)
405
+ abs(cmapEntry[i].green - g * 65535 / 7)
406
+ abs(cmapEntry[i].blue - b * 65535 / 3));
408
if (distance < minDistance) {
409
minDistance = distance;
415
BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel;
416
if (shared[nearestPixel] && !usedAsNearest[nearestPixel])
418
usedAsNearest[nearestPixel] = True;
424
/* Tidy up shared colours which we allocated but aren't going to use */
426
for (i = 0; i < cmapSize; i++) {
427
if (shared[i] && !usedAsNearest[i]) {
428
XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0);
432
fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed);
438
* AllocateExactBGR233Colours() attempts to allocate each of the colours in the
439
* BGR233 colour cube, stopping when an allocation fails. The order it does
440
* this in is such that we should get a fairly well spread subset of the cube,
441
* however many allocations are made. There's probably a neater algorithm for
442
* doing this, but it's not obvious to me anyway. The way this algorithm works
445
* At each stage, we introduce a new value for one of the primaries, and
446
* allocate all the colours with the new value of that primary and all previous
447
* values of the other two primaries. We start with r=0 as the "new" value
448
* for r, and g=0, b=0 as the "previous" values of g and b. So we get:
450
* New primary value Previous values of other primaries Colours allocated
451
* ----------------- ---------------------------------- -----------------
452
* r=0 g=0 b=0 r0 g0 b0
453
* g=7 r=0 b=0 r0 g7 b0
454
* b=3 r=0 g=0,7 r0 g0 b3
456
* r=7 g=0,7 b=0,3 r7 g0 b0
460
* g=3 r=0,7 b=0,3 r0 g3 b0
468
AllocateExactBGR233Colours()
470
int rv[] = {0,7,3,5,1,6,2,4};
471
int gv[] = {0,7,3,5,1,6,2,4};
472
int bv[] = {0,3,1,2};
478
nBGR233ColoursAllocated = 0;
485
for (gi = 0; gi < gn; gi++) {
486
for (bi = 0; bi < bn; bi++) {
487
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
497
for (ri = 0; ri < rn; ri++) {
498
for (bi = 0; bi < bn; bi++) {
499
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
508
for (ri = 0; ri < rn; ri++) {
509
for (gi = 0; gi < gn; gi++) {
510
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
521
* AllocateBGR233Colour() attempts to allocate the given BGR233 colour as a
522
* shared colormap entry, storing its pixel value in the BGR233ToPixel array.
523
* r is from 0 to 7, g from 0 to 7 and b from 0 to 3. It fails either when the
524
* allocation fails or when we would exceed the number of colours specified in
525
* the nColours resource.
529
AllocateBGR233Colour(int r, int g, int b)
533
if (nBGR233ColoursAllocated >= appData.nColours)
536
c.red = r * 65535 / 7;
537
c.green = g * 65535 / 7;
538
c.blue = b * 65535 / 3;
540
if (!XAllocColor(dpy, cmap, &c))
543
BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel;
545
nBGR233ColoursAllocated++;