summaryrefslogtreecommitdiffstats
path: root/krdc/vnc/colour.c
diff options
context:
space:
mode:
Diffstat (limited to 'krdc/vnc/colour.c')
-rw-r--r--krdc/vnc/colour.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/krdc/vnc/colour.c b/krdc/vnc/colour.c
new file mode 100644
index 00000000..a51d6e61
--- /dev/null
+++ b/krdc/vnc/colour.c
@@ -0,0 +1,415 @@
+/*
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * 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;
+}