mirror of
https://github.com/Stichting-MINIX-Research-Foundation/xsrc.git
synced 2025-09-10 05:07:33 -04:00
1603 lines
47 KiB
C
1603 lines
47 KiB
C
/*
|
|
* dpsXcmu.c -- Simple color management/allocation utility
|
|
*
|
|
* (c) Copyright 1988-1994 Adobe Systems Incorporated.
|
|
* All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sublicense this software
|
|
* and its documentation for any purpose and without fee is hereby granted,
|
|
* provided that the above copyright notices appear in all copies and that
|
|
* both those copyright notices and this permission notice appear in
|
|
* supporting documentation and that the name of Adobe Systems Incorporated
|
|
* not be used in advertising or publicity pertaining to distribution of the
|
|
* software without specific, written prior permission. No trademark license
|
|
* to use the Adobe trademarks is hereby granted. If the Adobe trademark
|
|
* "Display PostScript"(tm) is used to describe this software, its
|
|
* functionality or for any other purpose, such use shall be limited to a
|
|
* statement that this software works in conjunction with the Display
|
|
* PostScript system. Proper trademark attribution to reflect Adobe's
|
|
* ownership of the trademark shall be given whenever any such reference to
|
|
* the Display PostScript system is made.
|
|
*
|
|
* ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
|
|
* ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
* ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NON- INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE
|
|
* TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT
|
|
* PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
|
|
*
|
|
* Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
|
|
* Incorporated which may be registered in certain jurisdictions
|
|
*
|
|
* Portions Copyright 1989 by the Massachusetts Institute of Technology
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose and without fee is hereby granted, provided
|
|
* that the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation, and that the name of M.I.T. not be used in advertising
|
|
* or publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. M.I.T. makes no representations about the
|
|
* suitability of this software for any purpose. It is provided "as is"
|
|
* without express or implied warranty.
|
|
*
|
|
* M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
|
|
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Author: Adobe Systems Incorporated and Donna Converse, MIT X Consortium
|
|
*/
|
|
/* $XFree86: xc/lib/dps/dpsXcmu.c,v 1.5 2001/07/25 15:04:54 dawes Exp $ */
|
|
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xresource.h>
|
|
#include <X11/Xos.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <pwd.h>
|
|
|
|
#include "dpsassert.h"
|
|
#include "cslibint.h"
|
|
|
|
/* Defines for standard colormap routines */
|
|
|
|
#define PrivSort qsort
|
|
#include <stddef.h>
|
|
|
|
static char redsName[] = "reds";
|
|
static char greensName[] = "greens";
|
|
static char bluesName[] = "blues";
|
|
static char graysName[] = "grays";
|
|
|
|
/* Database containing DPS default color cube values */
|
|
|
|
typedef struct _dpyRec {
|
|
Display *dpy;
|
|
XrmDatabase db;
|
|
Atom XA_GRAY_DEFAULT_MAP;
|
|
struct _dpyRec *next;
|
|
} DpyRec;
|
|
|
|
static DpyRec *dpyRec = NULL;
|
|
static DpyRec *curDpyRec;
|
|
static DpyRec *FindDpyRec(Display *);
|
|
|
|
typedef struct {
|
|
unsigned long *pixels;
|
|
int npixels;
|
|
} PixelRec;
|
|
|
|
static Bool AllocateColor(Display *, Colormap, XColor *);
|
|
static Bool AllocateColormap(Display *, XStandardColormap *, XVisualInfo *, int *, PixelRec *, int *, int *, unsigned long);
|
|
static Bool CheckCube(XColor *, XColor *, XStandardColormap *);
|
|
static Bool CubicCube(XStandardColormap *);
|
|
static Bool GetColorCubeFromProperty(Display *, XVisualInfo *, XStandardColormap *, XStandardColormap **, int *);
|
|
static Bool GetGrayRampFromProperty(Display *, XVisualInfo *, XStandardColormap *, XStandardColormap **, int *);
|
|
Status XDPSCreateStandardColormaps(Display *, Drawable, Visual *, int, int, int, int, XStandardColormap *, XStandardColormap *, Bool);
|
|
static Status contiguous(unsigned long *, int, int *, unsigned long, int *, int *);
|
|
static XVisualInfo *PickCorrectVisual(Display *, XVisualInfo *, int, Colormap);
|
|
static int FindRampSize(XColor *, XColor *);
|
|
static long NumColors(char *, char *, char *);
|
|
static void AllocateColorCube(Display *, XVisualInfo *, XStandardColormap *, PixelRec *);
|
|
static void AllocateGrayRamp(Display *, XVisualInfo *, XStandardColormap *, XStandardColormap *, PixelRec *);
|
|
static void ColorValuesFromMask(unsigned long, unsigned long *, unsigned long *);
|
|
static void CreateDefaultsDb(Display *);
|
|
static void DefineProperty(Display *, XStandardColormap *, XVisualInfo *, XStandardColormap *, int, Atom);
|
|
static void FindStaticColorCube(Display *, XVisualInfo *, XStandardColormap *);
|
|
static void FindStaticGrayRamp(Display *, XVisualInfo *, XStandardColormap *, XStandardColormap *);
|
|
static void GetDatabaseValues(Display *, XVisualInfo *, XStandardColormap *, XStandardColormap *);
|
|
static void GetHomeDir(char *);
|
|
static void SetRamp(XColor *, XColor *, int, int *, unsigned long *);
|
|
static void ShrinkMapToFit(XStandardColormap *, int *, XVisualInfo *);
|
|
static void UseGrayCorners(XStandardColormap *, XStandardColormap *);
|
|
static void UseGrayDiagonal(XStandardColormap *, XStandardColormap *);
|
|
|
|
#define SCALE 65535
|
|
#undef ABS
|
|
#define ABS(x) ((x) < 0 ? -(x) : (x))
|
|
|
|
void XDPSGetDefaultColorMaps(
|
|
Display *dpy,
|
|
Screen *screen,
|
|
Drawable drawable,
|
|
XStandardColormap *colorCube,
|
|
XStandardColormap *grayRamp)
|
|
{
|
|
Window root;
|
|
Visual *visual;
|
|
XStandardColormap g;
|
|
|
|
/* If there is a screen specified, use it; otherwise use the drawable */
|
|
|
|
if (screen == NULL) {
|
|
if (drawable == None || ScreenCount(dpy) == 1) {
|
|
root = DefaultRootWindow(dpy);
|
|
screen = DefaultScreenOfDisplay(dpy);
|
|
} else {
|
|
/* Have to get the root for this drawable */
|
|
int x, y;
|
|
int i;
|
|
unsigned int width, height, bwidth, depth;
|
|
if (!XGetGeometry(dpy, drawable, &root, &x, &y, &width, &height,
|
|
&bwidth, &depth)) root = DefaultRootWindow(dpy);
|
|
for (i = 0;
|
|
i < ScreenCount(dpy) && root != RootWindow(dpy, i);
|
|
i++) {}
|
|
screen = ScreenOfDisplay(dpy, i);
|
|
}
|
|
} else root = RootWindowOfScreen(screen);
|
|
|
|
if (grayRamp == NULL) grayRamp = &g;
|
|
|
|
visual = DefaultVisualOfScreen(screen);
|
|
grayRamp->colormap = DefaultColormapOfScreen(screen);
|
|
if (colorCube != NULL) colorCube->colormap = grayRamp->colormap;
|
|
|
|
(void) XDPSCreateStandardColormaps(dpy, root, visual,
|
|
0, 0, 0, 0, colorCube, grayRamp, True);
|
|
}
|
|
|
|
Status XDPSCreateStandardColormaps(
|
|
Display *dpy,
|
|
Drawable drawable,
|
|
Visual *visual,
|
|
int reds, int greens, int blues, int grays,
|
|
XStandardColormap *colorCube,
|
|
XStandardColormap *grayRamp,
|
|
Bool retain)
|
|
{
|
|
XVisualInfo vtemp, *vinfo;
|
|
int nvis;
|
|
XStandardColormap *propCube = NULL, *propRamp = NULL;
|
|
int nPropCube = 0, nPropRamp = 0;
|
|
Bool gotCube = False, gotRamp;
|
|
PixelRec pixels;
|
|
|
|
if (grayRamp == NULL) return 0;
|
|
if (colorCube != NULL &&
|
|
colorCube->colormap != grayRamp->colormap) return 0;
|
|
|
|
if (dpy == NULL || (drawable == None && visual == NULL)) return 0;
|
|
|
|
if (visual == NULL) {
|
|
XWindowAttributes attr;
|
|
if (XGetWindowAttributes(dpy, drawable, &attr) == 0) return 0;
|
|
visual = attr.visual;
|
|
}
|
|
|
|
if (grayRamp->colormap == None && drawable == None) return 0;
|
|
|
|
curDpyRec = FindDpyRec(dpy);
|
|
if (curDpyRec == NULL) return 0;
|
|
|
|
vtemp.visualid = XVisualIDFromVisual(visual);
|
|
vinfo = XGetVisualInfo(dpy, VisualIDMask, &vtemp, &nvis);
|
|
if (nvis == 0) return 0;
|
|
|
|
if (nvis > 1) {
|
|
vinfo = PickCorrectVisual(dpy, vinfo, nvis, grayRamp->colormap);
|
|
}
|
|
if (vinfo == NULL) return 0;
|
|
|
|
if (grays <= 1) grayRamp->red_max = 0;
|
|
else grayRamp->red_max = grays - 1;
|
|
|
|
if (colorCube != NULL) {
|
|
if (reds <= 1) colorCube->red_max = 0;
|
|
else colorCube->red_max = reds - 1;
|
|
if (greens <= 1) colorCube->green_max = 0;
|
|
else colorCube->green_max = greens - 1;
|
|
if (blues <= 1) colorCube->blue_max = 0;
|
|
else colorCube->blue_max = blues - 1;
|
|
}
|
|
|
|
if ((vinfo->class == StaticGray || vinfo->class == GrayScale) &&
|
|
colorCube != NULL) {
|
|
/* Can't do a color cube in a gray visual! */
|
|
colorCube->red_max = colorCube->green_max = colorCube->blue_max =
|
|
colorCube->red_mult = colorCube->green_mult = colorCube->blue_mult =
|
|
colorCube->base_pixel = 0;
|
|
colorCube = NULL;
|
|
}
|
|
|
|
if (retain) {
|
|
Display *newDpy = XOpenDisplay(XDisplayString(dpy));
|
|
if (newDpy == NULL) retain = False;
|
|
else dpy = newDpy;
|
|
XGrabServer(dpy);
|
|
}
|
|
|
|
if (grayRamp->colormap == None) {
|
|
grayRamp->colormap = XCreateColormap(dpy, drawable, vinfo->visual,
|
|
AllocNone);
|
|
if (colorCube != NULL) colorCube->colormap = grayRamp->colormap;
|
|
}
|
|
|
|
if (colorCube != NULL) {
|
|
gotCube = GetColorCubeFromProperty(dpy, vinfo, colorCube,
|
|
&propCube, &nPropCube);
|
|
}
|
|
gotRamp = GetGrayRampFromProperty(dpy, vinfo, grayRamp,
|
|
&propRamp, &nPropRamp);
|
|
|
|
if (!gotRamp || (colorCube != NULL && !gotCube)) {
|
|
/* Couldn't find at least one thing we wanted, so let's look in the
|
|
database */
|
|
|
|
GetDatabaseValues(dpy, vinfo, colorCube, grayRamp);
|
|
|
|
pixels.pixels = NULL;
|
|
pixels.npixels = 0;
|
|
|
|
if (colorCube != NULL) {
|
|
if (colorCube->red_max != 0) {
|
|
AllocateColorCube(dpy, vinfo, colorCube, &pixels);
|
|
}
|
|
if (colorCube->red_max == 0) {
|
|
colorCube->green_max = colorCube->blue_max =
|
|
colorCube->red_mult = colorCube->green_mult =
|
|
colorCube->blue_mult = 0;
|
|
}
|
|
}
|
|
|
|
if (grayRamp->red_max != 0) {
|
|
AllocateGrayRamp(dpy, vinfo, grayRamp, colorCube, &pixels);
|
|
}
|
|
|
|
if (pixels.pixels != NULL) {
|
|
if (pixels.npixels != 0) {
|
|
XFreeColors(dpy, grayRamp->colormap,
|
|
pixels.pixels, pixels.npixels, 0);
|
|
}
|
|
free((char *) pixels.pixels);
|
|
}
|
|
|
|
if (retain) {
|
|
Pixmap p;
|
|
|
|
/* Create something to put in killid field so the entries can
|
|
be deleted sometime */
|
|
p = XCreatePixmap(dpy, RootWindow(dpy, vinfo->screen), 1, 1, 1);
|
|
if (colorCube != NULL && !gotCube && colorCube->red_max != 0) {
|
|
colorCube->visualid = vinfo->visualid;
|
|
colorCube->killid = p;
|
|
DefineProperty(dpy, colorCube, vinfo, propCube, nPropCube,
|
|
XA_RGB_DEFAULT_MAP);
|
|
}
|
|
if (!gotRamp && grayRamp->red_max != 0) {
|
|
grayRamp->visualid = vinfo->visualid;
|
|
grayRamp->killid = p;
|
|
DefineProperty(dpy, grayRamp, vinfo, propRamp, nPropRamp,
|
|
(vinfo->class == GrayScale ? XA_RGB_GRAY_MAP :
|
|
curDpyRec->XA_GRAY_DEFAULT_MAP));
|
|
}
|
|
XSetCloseDownMode(dpy, RetainTemporary);
|
|
}
|
|
}
|
|
|
|
if (grayRamp->red_max == 0) {
|
|
/* Use special magic values. If this is a default colormap,
|
|
the server recognizes a gray ramp with red_max=1, red_mult=1,
|
|
base_pixel=0 to mean a 2 gray ramp with BlackPixel being
|
|
the lowest intensity gray and WhitePixel being the highest
|
|
intensity gray. If it's not a default colormap, then the
|
|
server will either generate a BadValue error, or just happily
|
|
use pixel values 0 and 1; either is better than the alternative,
|
|
silently converting into a null device. */
|
|
grayRamp->red_max = 1;
|
|
grayRamp->red_mult = 1;
|
|
grayRamp->base_pixel = 0;
|
|
}
|
|
|
|
if (retain) {
|
|
XUngrabServer(dpy);
|
|
XCloseDisplay(dpy);
|
|
}
|
|
if (propCube != NULL) XFree((void *) propCube);
|
|
if (propRamp != NULL) XFree((void *) propRamp);
|
|
XFree((void *) vinfo);
|
|
return 1;
|
|
}
|
|
|
|
static DpyRec *FindDpyRec(Display *dpy)
|
|
{
|
|
DpyRec *d;
|
|
|
|
for (d = dpyRec; d != NULL; d = d->next) {
|
|
if (d->dpy == dpy) return d;
|
|
}
|
|
|
|
d = (DpyRec *) malloc(sizeof(DpyRec));
|
|
if (d == NULL) return NULL;
|
|
d->XA_GRAY_DEFAULT_MAP = XInternAtom(dpy, "DEFAULT_GRAY", False);
|
|
d->db = NULL;
|
|
d->next = dpyRec;
|
|
dpyRec = d;
|
|
return d;
|
|
}
|
|
|
|
static XVisualInfo *PickCorrectVisual(
|
|
Display *dpy,
|
|
XVisualInfo *vlist,
|
|
int n,
|
|
Colormap cmap)
|
|
{
|
|
register int i;
|
|
register int screen_number;
|
|
Bool def_cmap = False;
|
|
|
|
/* A visual id may be valid on multiple screens. Also, there may
|
|
* be multiple visuals with identical visual ids at different depths.
|
|
* If the colormap is the Default Colormap, use the Default Visual.
|
|
* Otherwise, arbitrarily, use the deepest visual.
|
|
*/
|
|
|
|
for (screen_number = ScreenCount(dpy); --screen_number >= 0; /**/) {
|
|
if (cmap == DefaultColormap(dpy, screen_number)) {
|
|
def_cmap = True;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (def_cmap) {
|
|
for (i = 0; i < n; i++, vlist++) {
|
|
if (vlist->visual == DefaultVisual(dpy, screen_number)) {
|
|
return vlist;
|
|
}
|
|
}
|
|
return NULL; /* Visual does not match colormap */
|
|
} else {
|
|
int maxdepth = 0;
|
|
XVisualInfo *v = 0;
|
|
|
|
for (i = 0; i < n; i++, vlist++) {
|
|
if (vlist->depth > maxdepth) {
|
|
maxdepth = vlist->depth;
|
|
v = vlist;
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
}
|
|
|
|
/* Do some rudimentary checking of the properties to avoid obviously bad ones.
|
|
How did they get there, anyway? */
|
|
|
|
static Bool ValidCube(
|
|
XStandardColormap *c,
|
|
XVisualInfo *vinfo)
|
|
{
|
|
unsigned long max = 1 << vinfo->depth;
|
|
unsigned long pixel;
|
|
|
|
if (c->red_max < 1 || c->green_max < 1 || c->blue_max < 1) return False;
|
|
if (c->base_pixel > max) return False;
|
|
pixel = (c->red_max * c->red_mult + c->green_max * c->green_mult +
|
|
c->blue_max * c->blue_mult + c->base_pixel) & 0xFFFFFFFF;
|
|
if (pixel > max) return False;
|
|
|
|
return True;
|
|
}
|
|
|
|
static Bool ValidRamp(
|
|
XStandardColormap *c,
|
|
XVisualInfo *vinfo)
|
|
{
|
|
unsigned long max = 1 << vinfo->depth;
|
|
unsigned long pixel;
|
|
|
|
if (c->red_max < 1) return False;
|
|
if (c->base_pixel > max) return False;
|
|
pixel = (c->red_max * c->red_mult + c->base_pixel) & 0xFFFFFFFF;
|
|
if (pixel > max) return False;
|
|
|
|
return True;
|
|
}
|
|
|
|
static Bool GetColorCubeFromProperty(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *colorCube,
|
|
XStandardColormap **cube,
|
|
int *ncube)
|
|
{
|
|
int gotCube;
|
|
int i;
|
|
register XStandardColormap *c;
|
|
|
|
gotCube = XGetRGBColormaps(dpy, RootWindow(dpy, vinfo->screen), cube,
|
|
ncube, XA_RGB_DEFAULT_MAP);
|
|
|
|
if (gotCube) {
|
|
/* Try to find a match with the visual */
|
|
c = *cube;
|
|
for (i = 0; i < *ncube; i++) {
|
|
if (c->colormap == colorCube->colormap &&
|
|
c->visualid == vinfo->visualid &&
|
|
ValidCube(c, vinfo)) {
|
|
colorCube->red_max = c->red_max;
|
|
colorCube->red_mult = c->red_mult;
|
|
colorCube->green_max = c->green_max;
|
|
colorCube->green_mult = c->green_mult;
|
|
colorCube->blue_max = c->blue_max;
|
|
colorCube->blue_mult = c->blue_mult;
|
|
colorCube->base_pixel = c->base_pixel;
|
|
colorCube->visualid = c->visualid;
|
|
colorCube->killid = c->killid;
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
if (i == *ncube) gotCube = False;
|
|
}
|
|
return gotCube;
|
|
}
|
|
|
|
static Bool GetGrayRampFromProperty(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *grayRamp,
|
|
XStandardColormap **ramp,
|
|
int *nramp)
|
|
{
|
|
int gotRamp;
|
|
int i;
|
|
Atom grayAtom;
|
|
register XStandardColormap *c;
|
|
|
|
if (vinfo->class == GrayScale) grayAtom = XA_RGB_GRAY_MAP;
|
|
else grayAtom = curDpyRec->XA_GRAY_DEFAULT_MAP;
|
|
|
|
gotRamp = XGetRGBColormaps(dpy, RootWindow(dpy, vinfo->screen), ramp,
|
|
nramp, grayAtom);
|
|
|
|
if (gotRamp) {
|
|
/* Try to find a match with the visual */
|
|
c = *ramp;
|
|
for (i = 0; i < *nramp; i++) {
|
|
if (c->colormap == grayRamp->colormap &&
|
|
c->visualid == vinfo->visualid &&
|
|
ValidRamp(c, vinfo)) {
|
|
grayRamp->red_max = c->red_max;
|
|
grayRamp->red_mult = c->red_mult;
|
|
grayRamp->base_pixel = c->base_pixel;
|
|
grayRamp->visualid = c->visualid;
|
|
grayRamp->killid = c->killid;
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
if (i == *nramp) gotRamp = False;
|
|
}
|
|
return gotRamp;
|
|
}
|
|
|
|
static void GetDatabaseValues(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *colorCube,
|
|
XStandardColormap *grayRamp)
|
|
{
|
|
char *class, *depth;
|
|
char namePrefix[40], classPrefix[40];
|
|
unsigned long max;
|
|
XStandardColormap fakeCube;
|
|
|
|
switch (vinfo->class) {
|
|
default:
|
|
case StaticGray: class = "StaticGray."; break;
|
|
case GrayScale: class = "GrayScale."; break;
|
|
case StaticColor: class = "StaticColor."; break;
|
|
case PseudoColor: class = "PseudoColor."; break;
|
|
case TrueColor: class = "TrueColor."; break;
|
|
case DirectColor: class = "DirectColor."; break;
|
|
}
|
|
|
|
if (vinfo->depth >= 24) depth = "24.";
|
|
else if (vinfo->depth >= 12) depth = "12.";
|
|
else if (vinfo->depth >= 8) depth = "8.";
|
|
else if (vinfo->depth >= 4) depth = "4.";
|
|
else if (vinfo->depth >= 2) depth = "2.";
|
|
else depth = "1.";
|
|
|
|
(void) strcpy(namePrefix, "dpsColorCube.");
|
|
(void) strcat(strcat(namePrefix, class), depth);
|
|
(void) strcpy(classPrefix, "DPSColorCube.");
|
|
(void) strcat(strcat(classPrefix, class), depth);
|
|
|
|
CreateDefaultsDb(dpy);
|
|
|
|
if (colorCube == NULL && vinfo->class == TrueColor) {
|
|
/* We'll need the color cube information to compute the gray ramp,
|
|
even if it wasn't asked for, so make colorCube point to a
|
|
temporary structure */
|
|
colorCube = &fakeCube;
|
|
}
|
|
|
|
if (colorCube != NULL) {
|
|
switch (vinfo->class) {
|
|
case StaticGray:
|
|
case GrayScale:
|
|
/* We can't do a color cube for these visuals */
|
|
break;
|
|
|
|
case TrueColor:
|
|
/* Rewrite whatever was there before with real values */
|
|
ColorValuesFromMask(vinfo->red_mask, &colorCube->red_max,
|
|
&colorCube->red_mult);
|
|
ColorValuesFromMask(vinfo->green_mask, &colorCube->green_max,
|
|
&colorCube->green_mult);
|
|
ColorValuesFromMask(vinfo->blue_mask, &colorCube->blue_max,
|
|
&colorCube->blue_mult);
|
|
colorCube->base_pixel = 0;
|
|
break;
|
|
|
|
case DirectColor:
|
|
/* Get the mults from the masks; ignore maxes */
|
|
ColorValuesFromMask(vinfo->red_mask, &max,
|
|
&colorCube->red_mult);
|
|
ColorValuesFromMask(vinfo->green_mask, &max,
|
|
&colorCube->green_mult);
|
|
ColorValuesFromMask(vinfo->blue_mask, &max,
|
|
&colorCube->blue_mult);
|
|
/* Get the maxes from the database */
|
|
if (colorCube->red_max == 0) {
|
|
colorCube->red_max =
|
|
NumColors(namePrefix, classPrefix, redsName) - 1;
|
|
}
|
|
if (colorCube->green_max == 0) {
|
|
colorCube->green_max =
|
|
NumColors(namePrefix, classPrefix, greensName) - 1;
|
|
}
|
|
if (colorCube->blue_max == 0) {
|
|
colorCube->blue_max =
|
|
NumColors(namePrefix, classPrefix, bluesName) - 1;
|
|
}
|
|
colorCube->base_pixel = 0;
|
|
break;
|
|
|
|
case PseudoColor:
|
|
if (colorCube->red_max == 0) {
|
|
colorCube->red_max =
|
|
NumColors(namePrefix, classPrefix, redsName) - 1;
|
|
}
|
|
if (colorCube->green_max == 0) {
|
|
colorCube->green_max =
|
|
NumColors(namePrefix, classPrefix, greensName) - 1;
|
|
}
|
|
if (colorCube->blue_max == 0) {
|
|
colorCube->blue_max =
|
|
NumColors(namePrefix, classPrefix, bluesName) - 1;
|
|
}
|
|
colorCube->red_mult = (colorCube->green_max + 1) *
|
|
(colorCube->blue_max + 1);
|
|
colorCube->green_mult = colorCube->blue_max + 1;
|
|
colorCube->blue_mult = 1;
|
|
break;
|
|
|
|
case StaticColor:
|
|
FindStaticColorCube(dpy, vinfo, colorCube);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (vinfo->class) {
|
|
case GrayScale:
|
|
case PseudoColor:
|
|
case DirectColor:
|
|
if (grayRamp->red_max == 0) {
|
|
grayRamp->red_max =
|
|
NumColors(namePrefix, classPrefix, graysName) - 1;
|
|
}
|
|
grayRamp->red_mult = 1;
|
|
break;
|
|
|
|
case TrueColor:
|
|
/* If the color cube is truly a cube, use its diagonal. Otherwise
|
|
were SOL and have to use a two-element ramp. */
|
|
if (CubicCube(colorCube)) UseGrayDiagonal(colorCube, grayRamp);
|
|
else UseGrayCorners(colorCube, grayRamp);
|
|
break;
|
|
|
|
case StaticColor:
|
|
case StaticGray:
|
|
FindStaticGrayRamp(dpy, vinfo, grayRamp, colorCube);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Bool CubicCube(XStandardColormap *cube)
|
|
{
|
|
return cube->red_max == cube->green_max && cube->red_max ==
|
|
cube->blue_max;
|
|
}
|
|
|
|
static void UseGrayDiagonal(XStandardColormap *cube, XStandardColormap *ramp)
|
|
{
|
|
ramp->red_max = cube->red_max;
|
|
ramp->red_mult = cube->red_mult + cube->green_mult + cube->blue_mult;
|
|
ramp->base_pixel = cube->base_pixel;
|
|
}
|
|
|
|
static void UseGrayCorners(XStandardColormap *cube, XStandardColormap *ramp)
|
|
{
|
|
ramp->red_max = 1;
|
|
ramp->red_mult = (cube->red_max + 1) * (cube->green_max + 1) *
|
|
(cube->blue_max + 1) - 1;
|
|
if (* (int *) &(cube->red_mult) < 0) ramp->red_mult *= -1;
|
|
ramp->base_pixel = cube->base_pixel;
|
|
}
|
|
|
|
static void ColorValuesFromMask(
|
|
unsigned long mask,
|
|
unsigned long *maxColor,
|
|
unsigned long *mult)
|
|
{
|
|
*mult = 1;
|
|
while ((mask & 1) == 0) {
|
|
*mult <<= 1;
|
|
mask >>= 1;
|
|
}
|
|
*maxColor = mask;
|
|
}
|
|
|
|
/*
|
|
Resource definitions for default color cube / gray ramp sizes
|
|
are based on visual class and depth. Working from least choices
|
|
to most, here's motivation for the defaults:
|
|
|
|
If unspecified, default is 0 values for red, green, and blue,
|
|
and 2 (black and white) for grays. This covers StaticGray, StaticColor,
|
|
and depths less than 4 of the other visual classes.
|
|
|
|
If we have a choice, we try to allocate a gray ramp with an odd number
|
|
of colors; this is so 50% gray can be rendered without dithering.
|
|
In general we don't want to allocate a large cube (even when many
|
|
colormap entries are available) because allocation of each entry
|
|
requires a round-trip to the server (entries allocated read-only
|
|
via XAllocColor).
|
|
|
|
For GrayScale, any depth less than 4 is treated as monochrome.
|
|
|
|
PseudoColor depth 4 we try for a 2x2x2 cube with the gray ramp on
|
|
the diagonal. Depth 8 uses a 4x4x4 cube with a separate 9 entry
|
|
gray ramp. Depth 12 uses a 6x6x5 "cube" with a separate 17 entry gray
|
|
ramp. The cube is non-symmetrical; we don't want to use the diagonal
|
|
for a gray ramp and we can get by with fewer blues than reds or greens.
|
|
|
|
For DirectColor, allocating a gray ramp separate from the color cube
|
|
is wasteful of map entries, so we specify a symmetrical cube and
|
|
share the diagonal entries for the gray ramp.
|
|
|
|
For TrueColor, # color shades is set equal to the # shades / primary;
|
|
we don't actually allocate map entries, but it's handy to be able to
|
|
do the resource lookup blindly and get the right value.
|
|
*/
|
|
|
|
static char dpsDefaults[] = "\
|
|
*reds: 0\n\
|
|
*greens: 0\n\
|
|
*blues: 0\n\
|
|
*grays: 2\n\
|
|
\
|
|
*GrayScale.4.grays: 9\n\
|
|
*GrayScale.8.grays: 17\n\
|
|
\
|
|
*PseudoColor.4.reds: 2\n\
|
|
*PseudoColor.4.greens: 2\n\
|
|
*PseudoColor.4.blues: 2\n\
|
|
*PseudoColor.4.grays: 2\n\
|
|
*PseudoColor.8.reds: 4\n\
|
|
*PseudoColor.8.greens: 4\n\
|
|
*PseudoColor.8.blues: 4\n\
|
|
*PseudoColor.8.grays: 9\n\
|
|
*PseudoColor.12.reds: 6\n\
|
|
*PseudoColor.12.greens: 6\n\
|
|
*PseudoColor.12.blues: 5\n\
|
|
*PseudoColor.12.grays: 17\n\
|
|
\
|
|
*DirectColor.8.reds: 4\n\
|
|
*DirectColor.8.greens: 4\n\
|
|
*DirectColor.8.blues: 4\n\
|
|
*DirectColor.8.grays: 4\n\
|
|
*DirectColor.12.reds: 6\n\
|
|
*DirectColor.12.greens: 6\n\
|
|
*DirectColor.12.blues: 6\n\
|
|
*DirectColor.12.grays: 6\n\
|
|
*DirectColor.24.reds: 7\n\
|
|
*DirectColor.24.greens: 7\n\
|
|
*DirectColor.24.blues: 7\n\
|
|
*DirectColor.24.grays: 7\n\
|
|
\
|
|
*TrueColor.12.reds: 16\n\
|
|
*TrueColor.12.greens: 16\n\
|
|
*TrueColor.12.blues: 16\n\
|
|
*TrueColor.12.grays: 16\n\
|
|
*TrueColor.24.reds: 256\n\
|
|
*TrueColor.24.greens: 256\n\
|
|
*TrueColor.24.blues: 256\n\
|
|
*TrueColor.24.grays: 256\n\
|
|
";
|
|
|
|
static XrmDatabase defaultDB = NULL;
|
|
|
|
static void CreateDefaultsDb(Display *dpy)
|
|
{
|
|
char home[256], *dpyDefaults;
|
|
|
|
if (defaultDB == NULL) defaultDB = XrmGetStringDatabase(dpsDefaults);
|
|
|
|
if (curDpyRec->db != NULL) return;
|
|
|
|
dpyDefaults = XResourceManagerString(dpy);
|
|
if (dpyDefaults != NULL) {
|
|
curDpyRec->db = XrmGetStringDatabase(dpyDefaults);
|
|
}
|
|
|
|
if (curDpyRec->db == NULL) {
|
|
GetHomeDir(home);
|
|
strcpy(home, "/.Xdefaults");
|
|
curDpyRec->db = XrmGetFileDatabase(home);
|
|
}
|
|
}
|
|
|
|
static void GetHomeDir(char *buf)
|
|
{
|
|
#ifndef X_NOT_POSIX
|
|
uid_t uid;
|
|
#else
|
|
int uid;
|
|
extern int getuid();
|
|
#ifndef SYSV386
|
|
extern struct passwd *getpwuid(), *getpwnam();
|
|
#endif
|
|
#endif
|
|
struct passwd *pw;
|
|
static char *ptr = NULL;
|
|
|
|
if (ptr == NULL) {
|
|
if (!(ptr = getenv("HOME"))) {
|
|
if ((ptr = getenv("USER")) != 0) pw = getpwnam(ptr);
|
|
else {
|
|
uid = getuid();
|
|
pw = getpwuid(uid);
|
|
}
|
|
if (pw) ptr = pw->pw_dir;
|
|
else {
|
|
ptr = NULL;
|
|
*buf = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ptr)
|
|
(void) strcpy(buf, ptr);
|
|
|
|
buf += strlen(buf);
|
|
*buf = '/';
|
|
buf++;
|
|
*buf = '\0';
|
|
return;
|
|
}
|
|
|
|
static long NumColors(char *namePrefix, char *classPrefix, char *color)
|
|
{
|
|
char name[40], class[40];
|
|
XrmValue rtnValue;
|
|
char *rtnType;
|
|
long value;
|
|
|
|
(void) strcpy(name, namePrefix);
|
|
(void) strcpy(class, classPrefix);
|
|
if (! XrmGetResource(curDpyRec->db, strcat(name, color),
|
|
strcat(class, color), &rtnType, &rtnValue)) {
|
|
if (! XrmGetResource(defaultDB, name, class, &rtnType, &rtnValue)) {
|
|
/* This should never happen, as our defaults cover all cases */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Resource value is number of shades of specified color. If value
|
|
is not an integer, atoi returns 0, so we return 0. If value
|
|
is less than 2, it is invalid (need at least 2 shades of a color).
|
|
Explicitly setting 0 is ok for colors (means to not use a color
|
|
cube) but merits a warning for gray. */
|
|
|
|
if (strcmp(rtnValue.addr, "0") == 0 && strcmp(color, "grays") != 0) {
|
|
return 0;
|
|
}
|
|
|
|
value = atol(rtnValue.addr);
|
|
if (value < 2) {
|
|
char mbuf[512];
|
|
sprintf(mbuf, "%% Value '%s' is invalid for %s resource\n",
|
|
rtnValue.addr, name);
|
|
DPSWarnProc(NULL, mbuf);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/* Query the entire colormap in the static color case, then try to find
|
|
a color cube. Check pairs of black and white cells trying to find
|
|
a cube between them and take the first one you find. */
|
|
|
|
static void FindStaticColorCube(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *colorCube)
|
|
{
|
|
XColor *ramp, *black, *white, *altBlack, *altWhite;
|
|
int i, entries;
|
|
|
|
entries = 1 << vinfo->depth;
|
|
ramp = (XColor *) calloc(entries, sizeof(XColor));
|
|
|
|
if (ramp == NULL) {
|
|
colorCube->red_max = 0;
|
|
return;
|
|
}
|
|
|
|
/* Query the colormap */
|
|
for (i = 0; i < entries; i++) ramp[i].pixel = i;
|
|
XQueryColors(dpy, colorCube->colormap, ramp, entries);
|
|
|
|
/* Find the white and black entries */
|
|
|
|
black = white = altBlack = altWhite = NULL;
|
|
for (i = 0; i < entries; i++) {
|
|
if (ramp[i].flags != (DoRed | DoBlue | DoGreen)) continue;
|
|
if (ramp[i].red == 0 && ramp[i].blue == 0 &&
|
|
ramp[i].green == 0) {
|
|
if (black == NULL) black = ramp+i;
|
|
else if (altBlack == NULL) altBlack = ramp+i;
|
|
} else if (ramp[i].red == SCALE && ramp[i].blue == SCALE &&
|
|
ramp[i].green == SCALE) {
|
|
if (white == NULL) white = ramp+i;
|
|
else if (altWhite == NULL) altWhite = ramp+i;
|
|
}
|
|
}
|
|
|
|
if (black == NULL || white == NULL) {
|
|
colorCube->red_max = 0;
|
|
free(ramp);
|
|
return;
|
|
}
|
|
|
|
/* Look for cubes between pairs of black & white */
|
|
if (!CheckCube(black, white, colorCube) &&
|
|
!CheckCube(altBlack, white, colorCube) &&
|
|
!CheckCube(black, altWhite, colorCube) &&
|
|
!CheckCube(altBlack, altWhite, colorCube)) {
|
|
colorCube->red_max = 0;
|
|
}
|
|
|
|
free(ramp);
|
|
}
|
|
|
|
#define R 1
|
|
#define G 2
|
|
#define B 4
|
|
#define C 8
|
|
#define M 16
|
|
#define Y 32
|
|
|
|
#define SMALLSCALE 255
|
|
#define CheckColor(color,r,g,b) ((((color)->red >> 8) == (r) * SMALLSCALE) && \
|
|
(((color)->green >> 8) == (g) * SMALLSCALE) && \
|
|
(((color)->blue >> 8) == (b) * SMALLSCALE))
|
|
|
|
static Bool CheckCube(
|
|
XColor *black,
|
|
XColor *white,
|
|
XStandardColormap *cube)
|
|
{
|
|
int r = 0, g = 0, b = 0, c = 0, m = 0, y = 0, k, w;
|
|
XColor *color;
|
|
unsigned int found = 0;
|
|
int small, middle, large;
|
|
int smallMult, smallMax, middleMult, middleMax, largeMult, largeMax;
|
|
Bool backwards = False;
|
|
int mult = 1;
|
|
XStandardColormap test; /* Test cube */
|
|
int i;
|
|
int size;
|
|
|
|
if (black == NULL || white == NULL) return False;
|
|
|
|
k = black->pixel;
|
|
w = white->pixel - k;
|
|
|
|
size = ABS(w);
|
|
if (w < 0) {
|
|
backwards = True;
|
|
mult = -1;
|
|
}
|
|
|
|
for (i = 1; i < size; i++) {
|
|
color = black + i*mult;
|
|
if (color->flags != (DoRed | DoBlue | DoGreen)) return False;
|
|
|
|
/* If black or white is in the middle of the cube, can't work */
|
|
if (CheckColor(color, 0, 0, 0)) return False;
|
|
if (CheckColor(color, 1, 1, 1)) return False;
|
|
|
|
/* Check for red, green, blue, cyan, magenta, and yellow */
|
|
if (CheckColor(color, 1, 0, 0)) {r = color->pixel-k; found |= R;}
|
|
else if (CheckColor(color, 0, 1, 0)) {g = color->pixel-k; found |= G;}
|
|
else if (CheckColor(color, 0, 0, 1)) {b = color->pixel-k; found |= B;}
|
|
else if (CheckColor(color, 0, 1, 1)) {c = color->pixel-k; found |= C;}
|
|
else if (CheckColor(color, 1, 0, 1)) {m = color->pixel-k; found |= M;}
|
|
else if (CheckColor(color, 1, 1, 0)) {y = color->pixel-k; found |= Y;}
|
|
}
|
|
|
|
/* If any color is missing no cube is possible */
|
|
if (found != (R | G | B | C | M | Y)) return False;
|
|
|
|
/* Next test. Make sure B + G = C, R + B = M, R + G = Y,
|
|
and R + G + B = W */
|
|
if (b + g != c) return False;
|
|
if (r + b != m) return False;
|
|
if (r + g != y) return False;
|
|
if (r + g + b != w) return False;
|
|
|
|
/* Looking good! Compensate for backwards cubes */
|
|
if (backwards) {
|
|
w = ABS(w);
|
|
r = ABS(r);
|
|
g = ABS(g);
|
|
b = ABS(b);
|
|
}
|
|
|
|
/* Find the smallest, middle, and largest difference */
|
|
if (r < b && b < g) {
|
|
small = r; middle = b; large = g;
|
|
} else if (r < g && g < b) {
|
|
small = r; middle = g; large = b;
|
|
} else if (b < r && r < g) {
|
|
small = b; middle = r; large = g;
|
|
} else if (b < g && g < r) {
|
|
small = b; middle = g; large = r;
|
|
} else if (g < r && r < b) {
|
|
small = g; middle = r; large = b;
|
|
} else {
|
|
small = g; middle = b; large = r;
|
|
}
|
|
|
|
/* The smallest must divide the middle, and the middle the large */
|
|
if ((middle % (small + 1)) != 0) return False;
|
|
if ((large % (small + middle + 1)) != 0) return False;
|
|
|
|
/* OK, we believe we have a cube. Compute the description */
|
|
smallMult = 1;
|
|
smallMax = small;
|
|
middleMult = small + 1;
|
|
middleMax = middle / middleMult;
|
|
largeMult = small + middle + 1;
|
|
largeMax = large / largeMult;
|
|
|
|
if (small == r) {
|
|
test.red_max = smallMax; test.red_mult = smallMult;
|
|
if (middle == b) {
|
|
test.blue_max = middleMax; test.blue_mult = middleMult;
|
|
test.green_max = largeMax; test.green_mult = largeMult;
|
|
} else {
|
|
test.green_max = middleMax; test.green_mult = middleMult;
|
|
test.blue_max = largeMax; test.blue_mult = largeMult;
|
|
}
|
|
} else if (small == g) {
|
|
test.green_max = smallMax; test.green_mult = smallMult;
|
|
if (middle == b) {
|
|
test.blue_max = middleMax; test.blue_mult = middleMult;
|
|
test.red_max = largeMax; test.red_mult = largeMult;
|
|
} else {
|
|
test.red_max = middleMax; test.red_mult = middleMult;
|
|
test.blue_max = largeMax; test.blue_mult = largeMult;
|
|
}
|
|
} else { /* small == b */
|
|
test.blue_max = smallMax; test.blue_mult = smallMult;
|
|
if (middle == r) {
|
|
test.red_max = middleMax; test.red_mult = middleMult;
|
|
test.green_max = largeMax; test.green_mult = largeMult;
|
|
} else {
|
|
test.green_max = middleMax; test.green_mult = middleMult;
|
|
test.red_max = largeMax; test.red_mult = largeMult;
|
|
}
|
|
}
|
|
|
|
/* Re-compensate for backwards cube */
|
|
if (backwards) {
|
|
test.red_mult *= -1;
|
|
test.green_mult *= -1;
|
|
test.blue_mult *= -1;
|
|
}
|
|
|
|
/* Finally, test the hypothesis! The answer must be correct within 1
|
|
bit. Only look at the top 8 bits; the others are too noisy */
|
|
|
|
for (i = 1; i < size; i++) {
|
|
#define calc(i, max, mult) ((((i / test.mult) % \
|
|
(test.max + 1)) * SCALE) / test.max)
|
|
r = ((unsigned short) calc(i, red_max, red_mult) >> 8) -
|
|
(black[i*mult].red >> 8);
|
|
g = ((unsigned short) calc(i, green_max, green_mult) >> 8) -
|
|
(black[i*mult].green >> 8);
|
|
b = ((unsigned short) calc(i, blue_max, blue_mult) >> 8) -
|
|
(black[i*mult].blue >> 8);
|
|
#undef calc
|
|
if (ABS(r) > 2 || ABS(g) > 2 || ABS(b) > 2) return False;
|
|
}
|
|
cube->red_max = test.red_max;
|
|
cube->red_mult = test.red_mult;
|
|
cube->green_max = test.green_max;
|
|
cube->green_mult = test.green_mult;
|
|
cube->blue_max = test.blue_max;
|
|
cube->blue_mult = test.blue_mult;
|
|
cube->base_pixel = k;
|
|
return True;
|
|
}
|
|
|
|
#undef R
|
|
#undef G
|
|
#undef B
|
|
#undef C
|
|
#undef M
|
|
#undef Y
|
|
|
|
/* Query the entire colormap in the static gray case, then try to find
|
|
a gray ramp. This handles there being 2 white or black entries
|
|
in the colormap and finds the longest linear ramp between pairs of
|
|
white and black. If there is a color cube, also check its diagonal and
|
|
use its corners if we need to */
|
|
|
|
static void FindStaticGrayRamp(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *grayRamp,
|
|
XStandardColormap *colorCube)
|
|
{
|
|
XColor *ramp, *black, *white, *altBlack, *altWhite;
|
|
int i, r0, r1, r2, r3, size, entries, redMult;
|
|
unsigned long base;
|
|
|
|
entries = 1 << vinfo->depth;
|
|
ramp = (XColor *) calloc(entries, sizeof(XColor));
|
|
|
|
if (ramp == NULL) {
|
|
grayRamp->red_max = 0;
|
|
return;
|
|
}
|
|
|
|
/* Query the colormap */
|
|
for (i = 0; i < entries; i++) ramp[i].pixel = i;
|
|
XQueryColors(dpy, grayRamp->colormap, ramp, entries);
|
|
|
|
/* Find the white and black entries */
|
|
|
|
black = white = altBlack = altWhite = NULL;
|
|
for (i = 0; i < entries; i++) {
|
|
if (ramp[i].flags != (DoRed | DoBlue | DoGreen)) continue;
|
|
if (CheckColor(ramp+i, 0, 0, 0)) {
|
|
if (black == NULL) black = ramp+i;
|
|
else if (altBlack == NULL) altBlack = ramp+i;
|
|
} else if (CheckColor(ramp+i, 1, 1, 1)) {
|
|
if (white == NULL) white = ramp+i;
|
|
else if (altWhite == NULL) altWhite = ramp+i;
|
|
}
|
|
}
|
|
|
|
if (black == NULL || white == NULL) {
|
|
grayRamp->red_max = 0;
|
|
free(ramp);
|
|
return;
|
|
}
|
|
|
|
/* Find out how large a ramp exists between pairs of black & white */
|
|
r0 = FindRampSize(black, white);
|
|
r1 = FindRampSize(altBlack, white);
|
|
r2 = FindRampSize(black, altWhite);
|
|
r3 = FindRampSize(altBlack, altWhite);
|
|
|
|
size = r0;
|
|
if (r1 > size) size = r1;
|
|
if (r2 > size) size = r2;
|
|
if (r3 > size) size = r3;
|
|
if (size == r0) SetRamp(black, white, size, &redMult, &base);
|
|
else if (size == r1) SetRamp(altBlack, white, size, &redMult, &base);
|
|
else if (size == r2) SetRamp(black, altWhite, size, &redMult, &base);
|
|
else if (size == r3) SetRamp(altBlack, altWhite, size, &redMult, &base);
|
|
|
|
if (colorCube != NULL && CubicCube(colorCube) &&
|
|
colorCube->red_max > size) {
|
|
UseGrayDiagonal(colorCube, grayRamp);
|
|
} else {
|
|
grayRamp->red_max = size;
|
|
grayRamp->red_mult = redMult;
|
|
grayRamp->base_pixel = base;
|
|
}
|
|
|
|
free(ramp);
|
|
}
|
|
|
|
static int FindRampSize(XColor *black, XColor *white)
|
|
{
|
|
XColor *c;
|
|
int r;
|
|
int mult = 1;
|
|
int i, size;
|
|
|
|
if (black == NULL || white == NULL) return 0;
|
|
size = ABS(white - black);
|
|
|
|
/* See if we have a backwards ramp */
|
|
if (black > white) mult = -1;
|
|
|
|
/* See if all cells between black and white are linear, to within 1 bit.
|
|
Only look at the high order 8 bits */
|
|
|
|
for (i = 1; i < size; i++) {
|
|
c = &black[i*mult];
|
|
if (c->red != c->blue || c->red != c->green) return 1;
|
|
r = ((unsigned short) ((i * SCALE) / size) >> 8) - (c->red >> 8);
|
|
if (ABS(r) > 2) return 1;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
static void SetRamp(
|
|
XColor *black,
|
|
XColor *white,
|
|
int size,
|
|
int *mult,
|
|
unsigned long *base)
|
|
{
|
|
*base = black->pixel;
|
|
*mult = (white - black) / size;
|
|
}
|
|
|
|
#define lowbit(x) ((x) & (~(x) + 1))
|
|
|
|
static unsigned long shiftdown(unsigned long x)
|
|
{
|
|
while ((x & 1) == 00) {
|
|
x = x >> 1;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
static void AllocateColorCube(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *colorCube,
|
|
PixelRec *pixels)
|
|
{
|
|
int count, first, remain, n, j;
|
|
unsigned long i;
|
|
Colormap cmap = colorCube->colormap;
|
|
unsigned long delta;
|
|
XColor color;
|
|
|
|
/* We do no allocation for TrueColor or StaticColor */
|
|
if (vinfo->class == TrueColor || vinfo->class == StaticColor) return;
|
|
|
|
if (vinfo->class == DirectColor) {
|
|
if ((i = shiftdown(vinfo->red_mask)) > colorCube->red_max)
|
|
colorCube->red_max = i;
|
|
if ((i = shiftdown(vinfo->green_mask)) > colorCube->green_max)
|
|
colorCube->green_max = i;
|
|
if ((i = shiftdown(vinfo->blue_mask)) > colorCube->blue_max)
|
|
colorCube->blue_max = i;
|
|
|
|
/* We only handle symmetric DirectColor */
|
|
count = colorCube->red_max + 1;
|
|
if (colorCube->blue_max + 1 < count) count = colorCube->blue_max + 1;
|
|
if (colorCube->green_max + 1 < count) count = colorCube->green_max + 1;
|
|
colorCube->red_max = colorCube->blue_max = colorCube->green_max =
|
|
count - 1;
|
|
|
|
delta = lowbit(vinfo->red_mask) + lowbit(vinfo->green_mask) +
|
|
lowbit(vinfo->blue_mask);
|
|
} else {
|
|
count = (colorCube->red_max + 1) * (colorCube->blue_max + 1) *
|
|
(colorCube->green_max + 1);
|
|
delta = 1;
|
|
}
|
|
|
|
colorCube->base_pixel = 0; /* temporary, may change */
|
|
|
|
pixels->pixels = (unsigned long *) calloc(vinfo->colormap_size,
|
|
sizeof(unsigned long));
|
|
if (pixels->pixels == NULL) {
|
|
colorCube->red_max = 0;
|
|
return;
|
|
}
|
|
|
|
if (!AllocateColormap(dpy, colorCube, vinfo, &count, pixels,
|
|
&first, &remain, delta)) {
|
|
free((char *) pixels->pixels);
|
|
pixels->pixels = NULL;
|
|
colorCube->red_max = 0;
|
|
return;
|
|
}
|
|
|
|
colorCube->base_pixel = pixels->pixels[first];
|
|
color.flags = DoRed | DoGreen | DoBlue;
|
|
|
|
/* Define colors */
|
|
for (n = 0, j = 0; j < count; ++j, n += delta) {
|
|
color.pixel = n + pixels->pixels[first];
|
|
if (vinfo->class == PseudoColor) {
|
|
#define calc(i, max, mult) ((((i / colorCube->mult) % \
|
|
(colorCube->max + 1)) * SCALE) / colorCube->max)
|
|
color.red = (unsigned short) calc(n, red_max, red_mult);
|
|
color.green = (unsigned short) calc(n, green_max, green_mult);
|
|
color.blue = (unsigned short) calc(n, blue_max, blue_mult);
|
|
#undef calc
|
|
} else {
|
|
color.red = color.green = color.blue =
|
|
(j * SCALE) / colorCube->red_max;
|
|
}
|
|
if (!AllocateColor(dpy, cmap, &color)) {
|
|
XFreeColors(dpy, cmap, pixels->pixels, count+first+remain, 0);
|
|
free((char *) pixels->pixels);
|
|
pixels->pixels = NULL;
|
|
colorCube->red_max = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Smush down unused pixels, if any */
|
|
|
|
for (j = 0; j < remain; j++) {
|
|
pixels->pixels[first+j] = pixels->pixels[first+count+j];
|
|
}
|
|
pixels->npixels -= count;
|
|
}
|
|
|
|
static void AllocateGrayRamp(
|
|
Display *dpy,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *grayRamp,
|
|
XStandardColormap *colorCube,
|
|
PixelRec *pixels)
|
|
{
|
|
int count, first, remain, n, i;
|
|
Colormap cmap = grayRamp->colormap;
|
|
XColor color;
|
|
unsigned long delta;
|
|
|
|
/* Allocate cells in read/write visuals only */
|
|
if (vinfo->class != PseudoColor && vinfo->class != GrayScale &&
|
|
vinfo->class != DirectColor) return;
|
|
|
|
if (vinfo->class == DirectColor) {
|
|
delta = lowbit(vinfo->red_mask) + lowbit(vinfo->green_mask) +
|
|
lowbit(vinfo->blue_mask);
|
|
} else delta = 1;
|
|
|
|
/* First of all see if there's a usable gray ramp in the color cube */
|
|
|
|
if (colorCube != NULL) {
|
|
if (CubicCube(colorCube)) {
|
|
if (colorCube->red_max >= grayRamp->red_max) {
|
|
/* diagonal is long enough! use it */
|
|
UseGrayDiagonal(colorCube, grayRamp);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
grayRamp->base_pixel = 0; /* temporary, may change */
|
|
|
|
count = grayRamp->red_max + 1;
|
|
|
|
if (pixels->pixels == NULL) {
|
|
pixels->pixels = (unsigned long *) calloc(vinfo->colormap_size,
|
|
sizeof(unsigned long));
|
|
if (pixels->pixels == NULL) {
|
|
grayRamp->red_max = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!AllocateColormap(dpy, grayRamp, vinfo, &count, pixels,
|
|
&first, &remain, delta)) {
|
|
/* Last gasp: try any diagonal or the corners of the color cube */
|
|
if (colorCube != NULL) {
|
|
if (CubicCube(colorCube)) UseGrayDiagonal(colorCube, grayRamp);
|
|
else UseGrayCorners(colorCube, grayRamp);
|
|
} else {
|
|
grayRamp->red_max = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
grayRamp->base_pixel = pixels->pixels[first];
|
|
color.flags = DoRed | DoGreen | DoBlue;
|
|
|
|
/* Define colors */
|
|
for (n = 0, i = 0; i < count; ++i, n += delta) {
|
|
color.pixel = n + pixels->pixels[first];
|
|
color.red = (unsigned short)((n * SCALE) / (grayRamp->red_max));
|
|
color.green = color.red;
|
|
color.blue = color.red;
|
|
|
|
if (!AllocateColor(dpy, cmap, &color)) {
|
|
/* Don't need to free pixels here; we'll do it on return */
|
|
grayRamp->red_max = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Smush down unused pixels, if any */
|
|
|
|
for (i = 0; i < remain; i++) {
|
|
pixels->pixels[first+i] = pixels->pixels[first+count+i];
|
|
}
|
|
pixels->npixels -= count;
|
|
}
|
|
|
|
static int compare(const void *a1, const void *a2)
|
|
{
|
|
register unsigned long *e1 = (unsigned long *) a1,
|
|
*e2 = (unsigned long *) a2;
|
|
|
|
if (*e1 < *e2) return -1;
|
|
if (*e1 > *e2) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static Bool AllocateColormap(
|
|
Display *dpy,
|
|
XStandardColormap *map,
|
|
XVisualInfo *vinfo,
|
|
int *count,
|
|
PixelRec *pixels,
|
|
int *first, int *remain,
|
|
unsigned long delta)
|
|
{
|
|
Colormap cmap = map->colormap;
|
|
int npixels, ok, i;
|
|
Bool success = False;
|
|
|
|
if (pixels->npixels == 0) {
|
|
/* First try to allocate the entire colormap */
|
|
npixels = vinfo->colormap_size;
|
|
ok = XAllocColorCells(dpy, cmap, 1, NULL, 0, pixels->pixels, npixels);
|
|
if (ok) success = True;
|
|
else {
|
|
int total;
|
|
int top, mid = 0;
|
|
|
|
/* If it's a gray ramp or direct color we need at least 2;
|
|
others 8 */
|
|
if (map->blue_max == 0 || vinfo->class == DirectColor) total = 2;
|
|
else total = 8;
|
|
|
|
/* Allocate all available cells, using binary backoff */
|
|
top = vinfo->colormap_size - 1;
|
|
while (total <= top) {
|
|
mid = total + ((top - total + 1) / 2);
|
|
ok = XAllocColorCells(dpy, cmap, 1, NULL, 0,
|
|
pixels->pixels, mid);
|
|
if (ok) {
|
|
if (mid == top) {
|
|
success = True;
|
|
break;
|
|
} else {
|
|
XFreeColors(dpy, cmap, pixels->pixels, mid, 0);
|
|
total = mid;
|
|
}
|
|
} else top = mid - 1;
|
|
}
|
|
if (success) npixels = mid;
|
|
else npixels = 0;
|
|
}
|
|
} else {
|
|
/* We must be in the gray ramp case, so we need at least 2 entries */
|
|
npixels = pixels->npixels;
|
|
if (map->blue_max != 0 || npixels >= 2) success = True;
|
|
}
|
|
|
|
if (success) {
|
|
/* Avoid pessimal case by testing to see if already sorted */
|
|
for (i = 0; i < npixels-1; ++i) {
|
|
if (pixels->pixels[i] != pixels->pixels[i+1]-1) break;
|
|
}
|
|
|
|
if (i < npixels-1) {
|
|
PrivSort((char *)pixels->pixels, npixels,
|
|
sizeof(unsigned long), compare);
|
|
}
|
|
|
|
if (!contiguous(pixels->pixels, npixels, count, delta,
|
|
first, remain)) {
|
|
/* If there are enough free cells, shrink the map to fit.
|
|
Otherwise fail; we'll free the pixels later */
|
|
if (((map->blue_max == 0 || vinfo->class == DirectColor) &&
|
|
*count >= 2) || *count >=8) {
|
|
ShrinkMapToFit(map, count, vinfo);
|
|
*remain = npixels - *first - *count;
|
|
} else success = False;
|
|
}
|
|
}
|
|
|
|
pixels->npixels = npixels;
|
|
return success;
|
|
}
|
|
|
|
static Bool contiguous(
|
|
unsigned long pixels[], /* specifies allocated pixels */
|
|
int npixels, /* specifies count of alloc'd pixels */
|
|
int *ncolors, /* specifies needed sequence length
|
|
If not available, returns max
|
|
available contiguous sequence */
|
|
unsigned long delta,
|
|
int *first, /* returns first index of sequence */
|
|
int *rem) /* returns first index after sequence,
|
|
* or 0, if none follow */
|
|
{
|
|
register int i = 1; /* walking index into the pixel array */
|
|
register int count = 1; /* length of sequence discovered so far */
|
|
int max = 1; /* longest sequence we found */
|
|
int maxstart = 0;
|
|
|
|
*first = 0;
|
|
while (count < *ncolors && i < npixels) {
|
|
if (pixels[i-1] + delta == pixels[i]) count++;
|
|
else {
|
|
if (count > max) {
|
|
max = count;
|
|
maxstart = *first;
|
|
}
|
|
count = 1;
|
|
*first = i;
|
|
}
|
|
i++;
|
|
}
|
|
if (i == npixels && count > max) {
|
|
max = count;
|
|
maxstart = *first;
|
|
}
|
|
*rem = npixels - i;
|
|
if (count != *ncolors) {
|
|
*ncolors = max;
|
|
*first = maxstart;
|
|
return False;
|
|
} return True;
|
|
}
|
|
|
|
static Bool AllocateColor(
|
|
Display *dpy,
|
|
Colormap cmap,
|
|
XColor *color)
|
|
{
|
|
unsigned long pix = color->pixel;
|
|
XColor request;
|
|
int ok;
|
|
|
|
request = *color;
|
|
|
|
/* Free RW, Alloc RO, if fails, try RW */
|
|
XFreeColors(dpy, cmap, &pix, 1, 0);
|
|
ok = XAllocColor(dpy, cmap, &request);
|
|
|
|
/* If the pixel we get back isn't the request one, probably RO
|
|
White or Black, so shove it in RW so our cube is correct.
|
|
If alloc fails, try RW. */
|
|
|
|
if (!ok || request.pixel != color->pixel) {
|
|
ok = XAllocColorCells(dpy, cmap, 0, NULL, 0, &pix, 1);
|
|
|
|
if (pix != color->pixel) XFreeColors(dpy, cmap, &pix, 1, 0);
|
|
if (!ok || pix != color->pixel) {
|
|
return False;
|
|
}
|
|
request = *color;
|
|
XStoreColor(dpy, cmap, &request);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
static void ShrinkMapToFit(
|
|
XStandardColormap *map,
|
|
int *space,
|
|
XVisualInfo *vinfo)
|
|
{
|
|
if (map->blue_max == 0) map->red_max = *space - 1;
|
|
else if (vinfo->class == DirectColor) {
|
|
if (map->red_max > *space - 1) map->red_max = *space - 1;
|
|
if (map->green_max > *space - 1) map->green_max = *space - 1;
|
|
if (map->blue_max > *space - 1) map->blue_max = *space - 1;
|
|
} else {
|
|
int which = 2;
|
|
while ((map->red_max + 1) * (map->green_max + 1) *
|
|
(map->blue_max + 1) > *space) {
|
|
if (which == 0) {
|
|
if (map->red_max > 1) map->red_max--;
|
|
which = 1;
|
|
} else if (which == 1) {
|
|
if (map->green_max > 1) map->green_max--;
|
|
which = 2;
|
|
} else {
|
|
if (map->blue_max > 1) map->blue_max--;
|
|
which = 0;
|
|
}
|
|
}
|
|
*space = (map->red_max + 1) * (map->green_max + 1) *
|
|
(map->blue_max + 1);
|
|
|
|
map->red_mult = (map->green_max + 1) * (map->blue_max + 1);
|
|
map->green_mult = map->blue_max + 1;
|
|
map->blue_mult = 1;
|
|
}
|
|
}
|
|
|
|
static void DefineProperty(
|
|
Display *dpy,
|
|
XStandardColormap *map,
|
|
XVisualInfo *vinfo,
|
|
XStandardColormap *prop,
|
|
int nProp,
|
|
Atom atom)
|
|
{
|
|
XStandardColormap *copy;
|
|
int i;
|
|
|
|
if (nProp == 0) {
|
|
XSetRGBColormaps(dpy, RootWindow(dpy, vinfo->screen), map, 1, atom);
|
|
return;
|
|
}
|
|
|
|
copy = (XStandardColormap *) calloc(nProp+1, sizeof(XStandardColormap));
|
|
|
|
/* Hm. If I can't allocate the list, is it better to just put our
|
|
property on, or to leave the ones there? I'll guess the latter... */
|
|
if (copy == NULL) return;
|
|
|
|
if (vinfo->visual == DefaultVisual(dpy, vinfo->screen) &&
|
|
map->colormap == DefaultColormap(dpy, vinfo->screen)) {
|
|
/* Put new entry first; it's more likely to be useful */
|
|
for (i = 0; i < nProp; i++) copy[i+1] = prop[i];
|
|
i = 0;
|
|
} else {
|
|
/* Put it at the end */
|
|
for (i = 0; i < nProp; i++) copy[i] = prop[i];
|
|
/* i = nProp; (it does already) */
|
|
}
|
|
|
|
copy[i] = *map;
|
|
XSetRGBColormaps(dpy, RootWindow(dpy, vinfo->screen), copy, nProp+1, atom);
|
|
|
|
free((void *) copy);
|
|
}
|