|
|
|
/*
|
|
|
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* This is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this software; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
|
* USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* colour.c - functions to deal with colour - i.e. RFB pixel formats, X visuals
|
|
|
|
* and colormaps. Thanks to Grant McDorman for some of the ideas used here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vncviewer.h"
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define INVALID_PIXEL 0xffffffff
|
|
|
|
#define MAX_CMAP_SIZE 256
|
|
|
|
#define BGR233_SIZE 256
|
|
|
|
unsigned long BGR233ToPixel[BGR233_SIZE];
|
|
|
|
|
|
|
|
Colormap cmap;
|
|
|
|
Visual *vis;
|
|
|
|
unsigned int visdepth, visbpp;
|
|
|
|
Bool allocColorFailed = False;
|
|
|
|
|
|
|
|
static int nBGR233ColoursAllocated;
|
|
|
|
|
|
|
|
static int GetBPPForDepth(int depth);
|
|
|
|
static void SetupBGR233Map(void);
|
|
|
|
static void AllocateExactBGR233Colours(void);
|
|
|
|
static Bool AllocateBGR233Colour(int r, int g, int b);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are
|
|
|
|
* equivalent to the RFB protocol's "pixel format"). Having decided on the
|
|
|
|
* best visual, it also creates a colormap if necessary, sets the appropriate
|
|
|
|
* resources on the toplevel widget, and sets up the myFormat structure to
|
|
|
|
* describe the pixel format in terms that the RFB server will be able to
|
|
|
|
* understand.
|
|
|
|
*
|
|
|
|
* The algorithm for deciding which visual to use is as follows:
|
|
|
|
*
|
|
|
|
* If forceOwnCmap is true then we try to use a PseudoColor visual - we first
|
|
|
|
* see if there's one of the same depth as the RFB server, followed by an 8-bit
|
|
|
|
* deep one.
|
|
|
|
*
|
|
|
|
* If forceTrueColour is true then we try to use a TrueColor visual - if
|
|
|
|
* requestedDepth is set then it must be of that depth, otherwise any depth
|
|
|
|
* will be used.
|
|
|
|
*
|
|
|
|
* Otherwise, we use the X server's default visual and colormap. If this is
|
|
|
|
* TrueColor then we just ask the RFB server for this format. If the default
|
|
|
|
* isn't TrueColor, or if useBGR233 is true, then we ask the RFB server for
|
|
|
|
* BGR233 pixel format and use a lookup table to translate to the nearest
|
|
|
|
* colours provided by the X server.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
SetVisualAndCmap()
|
|
|
|
{
|
|
|
|
/* just use default visual and colormap */
|
|
|
|
|
|
|
|
vis = DefaultVisual(dpy,DefaultScreen(dpy));
|
|
|
|
visdepth = DefaultDepth(dpy,DefaultScreen(dpy));
|
|
|
|
visbpp = GetBPPForDepth(visdepth);
|
|
|
|
cmap = DefaultColormap(dpy,DefaultScreen(dpy));
|
|
|
|
|
|
|
|
if (!appData.useBGR233 && (vis->class == TrueColor)) {
|
|
|
|
|
|
|
|
myFormat.bitsPerPixel = visbpp;
|
|
|
|
myFormat.depth = visdepth;
|
|
|
|
myFormat.trueColour = 1;
|
|
|
|
myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
|
|
|
|
myFormat.redShift = ffs(vis->red_mask) - 1;
|
|
|
|
myFormat.greenShift = ffs(vis->green_mask) - 1;
|
|
|
|
myFormat.blueShift = ffs(vis->blue_mask) - 1;
|
|
|
|
myFormat.redMax = vis->red_mask >> myFormat.redShift;
|
|
|
|
myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
|
|
|
|
myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
|
|
|
|
|
|
|
|
fprintf(stderr,
|
|
|
|
"Using default colormap which is TrueColor. Pixel format:\n");
|
|
|
|
PrintPixelFormat(&myFormat);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
appData.useBGR233 = True;
|
|
|
|
|
|
|
|
myFormat.bitsPerPixel = 8;
|
|
|
|
myFormat.depth = 8;
|
|
|
|
myFormat.trueColour = 1;
|
|
|
|
myFormat.bigEndian = 0;
|
|
|
|
myFormat.redMax = 7;
|
|
|
|
myFormat.greenMax = 7;
|
|
|
|
myFormat.blueMax = 3;
|
|
|
|
myFormat.redShift = 0;
|
|
|
|
myFormat.greenShift = 3;
|
|
|
|
myFormat.blueShift = 6;
|
|
|
|
|
|
|
|
fprintf(stderr,
|
|
|
|
"Using default colormap and translating from BGR233. Pixel format:\n");
|
|
|
|
PrintPixelFormat(&myFormat);
|
|
|
|
|
|
|
|
SetupBGR233Map();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GetBPPForDepth looks through the "pixmap formats" to find the bits-per-pixel
|
|
|
|
* for the given depth.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
GetBPPForDepth(int depth)
|
|
|
|
{
|
|
|
|
XPixmapFormatValues *format;
|
|
|
|
int nformats;
|
|
|
|
int i;
|
|
|
|
int bpp;
|
|
|
|
|
|
|
|
format = XListPixmapFormats(dpy, &nformats);
|
|
|
|
|
|
|
|
for (i = 0; i < nformats; i++) {
|
|
|
|
if (format[i].depth == depth)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == nformats) {
|
|
|
|
fprintf(stderr,"no pixmap format for depth %d???\n", depth);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bpp = format[i].bits_per_pixel;
|
|
|
|
|
|
|
|
XFree(format);
|
|
|
|
|
|
|
|
if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) {
|
|
|
|
fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return bpp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SetupBGR233Map() sets up the BGR233ToPixel array.
|
|
|
|
*
|
|
|
|
* It calls AllocateExactBGR233Colours to allocate some exact BGR233 colours
|
|
|
|
* (limited by space in the colormap and/or by the value of the nColours
|
|
|
|
* resource). If the number allocated is less than BGR233_SIZE then it fills
|
|
|
|
* the rest in using the "nearest" colours available. How this is done depends
|
|
|
|
* on the value of the useSharedColours resource. If it's false, we use only
|
|
|
|
* colours from the exact BGR233 colours we've just allocated. If it's true,
|
|
|
|
* then we also use other clients' "shared" colours available in the colormap.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
SetupBGR233Map(void)
|
|
|
|
{
|
|
|
|
int r, g, b;
|
|
|
|
long i;
|
|
|
|
unsigned long nearestPixel = 0;
|
|
|
|
int cmapSize;
|
|
|
|
XColor cmapEntry[MAX_CMAP_SIZE];
|
|
|
|
Bool exactBGR233[MAX_CMAP_SIZE];
|
|
|
|
Bool shared[MAX_CMAP_SIZE];
|
|
|
|
Bool usedAsNearest[MAX_CMAP_SIZE];
|
|
|
|
int nSharedUsed = 0;
|
|
|
|
|
|
|
|
if (visdepth > 8) {
|
|
|
|
appData.nColours = 256; /* ignore nColours setting for > 8-bit deep */
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < BGR233_SIZE; i++) {
|
|
|
|
BGR233ToPixel[i] = INVALID_PIXEL;
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocateExactBGR233Colours();
|
|
|
|
|
|
|
|
fprintf(stderr,"Got %d exact BGR233 colours out of %d\n",
|
|
|
|
nBGR233ColoursAllocated, appData.nColours);
|
|
|
|
|
|
|
|
if (nBGR233ColoursAllocated < BGR233_SIZE) {
|
|
|
|
|
|
|
|
if (visdepth > 8) { /* shouldn't get here */
|
|
|
|
fprintf(stderr,"Error: couldn't allocate BGR233 colours even though "
|
|
|
|
"depth is %d\n", visdepth);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
cmapSize = (1 << visdepth);
|
|
|
|
|
|
|
|
for (i = 0; i < cmapSize; i++) {
|
|
|
|
cmapEntry[i].pixel = i;
|
|
|
|
exactBGR233[i] = False;
|
|
|
|
shared[i] = False;
|
|
|
|
usedAsNearest[i] = False;
|
|
|
|
}
|
|
|
|
|
|
|
|
XQueryColors(dpy, cmap, cmapEntry, cmapSize);
|
|
|
|
|
|
|
|
/* mark all our exact BGR233 pixels */
|
|
|
|
|
|
|
|
for (i = 0; i < BGR233_SIZE; i++) {
|
|
|
|
if (BGR233ToPixel[i] != INVALID_PIXEL)
|
|
|
|
exactBGR233[BGR233ToPixel[i]] = True;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (appData.useSharedColours) {
|
|
|
|
|
|
|
|
/* Try to find existing shared colours. This is harder than it sounds
|
|
|
|
because XQueryColors doesn't tell us whether colours are shared,
|
|
|
|
private or unallocated. What we do is go through the colormap and for
|
|
|
|
each pixel try to allocate exactly its RGB values. If this returns a
|
|
|
|
different pixel then it's definitely either a private or unallocated
|
|
|
|
pixel, so no use to us. If it returns us the same pixel again, then
|
|
|
|
it's likely that it's a shared colour - however, it is possible that
|
|
|
|
it was actually an unallocated pixel, which we've now allocated. We
|
|
|
|
minimise this possibility by going through the pixels in reverse order
|
|
|
|
- this helps becuse the X server allocates new pixels from the lowest
|
|
|
|
number up, so it should only be a problem for the lowest unallocated
|
|
|
|
pixel. Got that? */
|
|
|
|
|
|
|
|
for (i = cmapSize-1; i >= 0; i--) {
|
|
|
|
if (!exactBGR233[i] &&
|
|
|
|
XAllocColor(dpy, cmap, &cmapEntry[i])) {
|
|
|
|
|
|
|
|
if (cmapEntry[i].pixel == (unsigned long) i) {
|
|
|
|
|
|
|
|
shared[i] = True; /* probably shared */
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
/* "i" is either unallocated or private. We have now unnecessarily
|
|
|
|
allocated cmapEntry[i].pixel. Free it. */
|
|
|
|
|
|
|
|
XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now fill in the nearest colours */
|
|
|
|
|
|
|
|
for (r = 0; r < 8; r++) {
|
|
|
|
for (g = 0; g < 8; g++) {
|
|
|
|
for (b = 0; b < 4; b++) {
|
|
|
|
if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) {
|
|
|
|
|
|
|
|
unsigned long minDistance = ULONG_MAX;
|
|
|
|
|
|
|
|
for (i = 0; i < cmapSize; i++) {
|
|
|
|
if (exactBGR233[i] || shared[i]) {
|
|
|
|
unsigned long distance
|
|
|
|
= (abs(cmapEntry[i].red - r * 65535 / 7)
|
|
|
|
+ abs(cmapEntry[i].green - g * 65535 / 7)
|
|
|
|
+ abs(cmapEntry[i].blue - b * 65535 / 3));
|
|
|
|
|
|
|
|
if (distance < minDistance) {
|
|
|
|
minDistance = distance;
|
|
|
|
nearestPixel = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel;
|
|
|
|
if (shared[nearestPixel] && !usedAsNearest[nearestPixel])
|
|
|
|
nSharedUsed++;
|
|
|
|
usedAsNearest[nearestPixel] = True;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tidy up shared colours which we allocated but aren't going to use */
|
|
|
|
|
|
|
|
for (i = 0; i < cmapSize; i++) {
|
|
|
|
if (shared[i] && !usedAsNearest[i]) {
|
|
|
|
XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AllocateExactBGR233Colours() attempts to allocate each of the colours in the
|
|
|
|
* BGR233 colour cube, stopping when an allocation fails. The order it does
|
|
|
|
* this in is such that we should get a fairly well spread subset of the cube,
|
|
|
|
* however many allocations are made. There's probably a neater algorithm for
|
|
|
|
* doing this, but it's not obvious to me anyway. The way this algorithm works
|
|
|
|
* is:
|
|
|
|
*
|
|
|
|
* At each stage, we introduce a new value for one of the primaries, and
|
|
|
|
* allocate all the colours with the new value of that primary and all previous
|
|
|
|
* values of the other two primaries. We start with r=0 as the "new" value
|
|
|
|
* for r, and g=0, b=0 as the "previous" values of g and b. So we get:
|
|
|
|
*
|
|
|
|
* New primary value Previous values of other primaries Colours allocated
|
|
|
|
* ----------------- ---------------------------------- -----------------
|
|
|
|
* r=0 g=0 b=0 r0 g0 b0
|
|
|
|
* g=7 r=0 b=0 r0 g7 b0
|
|
|
|
* b=3 r=0 g=0,7 r0 g0 b3
|
|
|
|
* r0 g7 b3
|
|
|
|
* r=7 g=0,7 b=0,3 r7 g0 b0
|
|
|
|
* r7 g0 b3
|
|
|
|
* r7 g7 b0
|
|
|
|
* r7 g7 b3
|
|
|
|
* g=3 r=0,7 b=0,3 r0 g3 b0
|
|
|
|
* r0 g3 b3
|
|
|
|
* r7 g3 b0
|
|
|
|
* r7 g3 b3
|
|
|
|
* ....etc.
|
|
|
|
* */
|
|
|
|
|
|
|
|
static void
|
|
|
|
AllocateExactBGR233Colours(void)
|
|
|
|
{
|
|
|
|
int rv[] = {0,7,3,5,1,6,2,4};
|
|
|
|
int gv[] = {0,7,3,5,1,6,2,4};
|
|
|
|
int bv[] = {0,3,1,2};
|
|
|
|
int rn = 0;
|
|
|
|
int gn = 1;
|
|
|
|
int bn = 1;
|
|
|
|
int ri, gi, bi;
|
|
|
|
|
|
|
|
nBGR233ColoursAllocated = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (rn == 8)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ri = rn;
|
|
|
|
for (gi = 0; gi < gn; gi++) {
|
|
|
|
for (bi = 0; bi < bn; bi++) {
|
|
|
|
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rn++;
|
|
|
|
|
|
|
|
if (gn == 8)
|
|
|
|
break;
|
|
|
|
|
|
|
|
gi = gn;
|
|
|
|
for (ri = 0; ri < rn; ri++) {
|
|
|
|
for (bi = 0; bi < bn; bi++) {
|
|
|
|
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gn++;
|
|
|
|
|
|
|
|
if (bn < 4) {
|
|
|
|
|
|
|
|
bi = bn;
|
|
|
|
for (ri = 0; ri < rn; ri++) {
|
|
|
|
for (gi = 0; gi < gn; gi++) {
|
|
|
|
if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bn++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AllocateBGR233Colour() attempts to allocate the given BGR233 colour as a
|
|
|
|
* shared colormap entry, storing its pixel value in the BGR233ToPixel array.
|
|
|
|
* r is from 0 to 7, g from 0 to 7 and b from 0 to 3. It fails either when the
|
|
|
|
* allocation fails or when we would exceed the number of colours specified in
|
|
|
|
* the nColours resource.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
AllocateBGR233Colour(int r, int g, int b)
|
|
|
|
{
|
|
|
|
XColor c;
|
|
|
|
|
|
|
|
if (nBGR233ColoursAllocated >= appData.nColours)
|
|
|
|
return False;
|
|
|
|
|
|
|
|
c.red = r * 65535 / 7;
|
|
|
|
c.green = g * 65535 / 7;
|
|
|
|
c.blue = b * 65535 / 3;
|
|
|
|
|
|
|
|
if (!XAllocColor(dpy, cmap, &c))
|
|
|
|
return False;
|
|
|
|
|
|
|
|
BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel;
|
|
|
|
|
|
|
|
nBGR233ColoursAllocated++;
|
|
|
|
|
|
|
|
return True;
|
|
|
|
}
|