home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vsiftp.vmssoftware.com
/
VSIPUBLIC@vsiftp.vmssoftware.com.tar
/
FREEWARE
/
FREEWARE40.ZIP
/
xpaint-247
/
palette.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-01-03
|
18KB
|
745 lines
/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */
/* | | */
/* | Permission to use, copy, modify, and to distribute this software | */
/* | and its documentation for any purpose is hereby granted without | */
/* | fee, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. There is no | */
/* | representations about the suitability of this software for | */
/* | any purpose. this software is provided "as is" without express | */
/* | or implied warranty. | */
/* | | */
/* +-------------------------------------------------------------------+ */
/* $Id: palette.c,v 1.3 1996/04/19 08:53:26 torsten Exp $ */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include "palette.h"
#include "hash.h"
#include "misc.h"
#include "image.h"
#include "xpaint.h"
typedef struct list_s {
Display *dpy;
Colormap cmap;
Palette *map;
struct list_s *next;
} PaletteList;
typedef struct Col_s {
XColor color;
Boolean used;
Boolean invalid;
} Col;
#define HASH_SIZE 128
#define HASH(c) ((int)((c).red + (c).green + (c).blue) % HASH_SIZE)
#define HASH_PIXEL(c) ((c) % HASH_SIZE)
static PaletteList *cmapList = NULL;
static int
readCMP(void *cca, void *ccb)
{
XColor *ca = &((Col *) cca)->color, *cb = &((Col *) ccb)->color;
if (ca->red != cb->red)
return ca->red < cb->red ? -1 : 1;
if (ca->green != cb->green)
return ca->green < cb->green ? -1 : 1;
if (ca->blue != cb->blue)
return ca->blue < cb->blue ? -1 : 1;
return 0;
}
static int
cmpPixel(void *cca, void *ccb)
{
if (((Col *) cca)->color.pixel == ((Col *) ccb)->color.pixel)
return 0;
return (((Col *) cca)->color.pixel < ((Col *) ccb)->color.pixel) ? -1 : 1;
}
static void
freeFunc(void *junk)
{
/*
** Nop free func
*/
}
static void
entryUnlink(Palette * map, Col * node)
{
node->used = True;
node->invalid = False;
map->nfree--;
}
#define STEP 256
static Palette *
paletteNew(Widget w, Boolean useDefault)
{
int i, v;
Display *dpy = XtDisplay(w);
Screen *screen = XtScreen(w);
Colormap rcmap = DefaultColormapOfScreen(screen);
Palette *map = XtNew(Palette);
Col *ctable;
Visual *visual = NULL;
PaletteList *new;
int end, depth = -1;
Boolean flg = False;
XtVaGetValues(w, XtNvisual, &visual, XtNdepth, &depth, NULL);
if (visual == NULL || useDefault)
visual = DefaultVisualOfScreen(screen);
if (depth <= 0 || useDefault)
depth = DefaultDepthOfScreen(screen);
map->htable = NULL;
map->ltable = NULL;
map->display = dpy;
map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
map->list = NULL;
map->last = NULL;
map->isMapped = visual->class != TrueColor;
map->isGrey = (visual->class == StaticGray || visual->class == GrayScale);
map->ncolors = visual->map_entries;
map->nfree = 0;
map->ctable = NULL;
map->visual = visual;
map->depth = depth;
map->userList = NULL;
map->isDefault = False;
map->mine = None;
switch (visual->class) {
case TrueColor:
map->rShift = 0;
map->gShift = 0;
map->bShift = 0;
map->rRange = 1;
map->gRange = 1;
map->bRange = 1;
for (v = visual->red_mask; (v & 1) == 0; v >>= 1)
map->rShift++;
for (; (v & 1) == 1; v >>= 1)
map->rRange <<= 1;
for (v = visual->green_mask; (v & 1) == 0; v >>= 1)
map->gShift++;
for (; (v & 1) == 1; v >>= 1)
map->gRange <<= 1;
for (v = visual->blue_mask; (v & 1) == 0; v >>= 1)
map->bShift++;
for (; (v & 1) == 1; v >>= 1)
map->bRange <<= 1;
case StaticGray:
case StaticColor:
#ifndef VMS
map->readonly = True;
#else
map->Readonly = True;
#endif
map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
visual, AllocNone);
map->isDefault = False;
goto addlist;
default:
#ifndef VMS
map->readonly = False;
#else
map->Readonly = False;
#endif
if (useDefault) {
map->cmap = rcmap;
map->isDefault = True;
} else {
map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
visual, AllocAll);
}
break;
}
ctable = (Col *) XtCalloc(sizeof(Col), visual->map_entries);
map->ctable = ctable;
end = CellsOfScreen(screen);
for (i = 0; i < visual->map_entries; i += STEP) {
XColor xcol[STEP];
int cnt = visual->map_entries - i;
int j, d;
if (cnt > STEP)
cnt = STEP;
for (j = 0; j < cnt; j++) {
Col *c = &ctable[i + j];
c->color.pixel = i + j;
c->color.flags = DoRed | DoGreen | DoBlue;
xcol[j].pixel = i + j;
xcol[j].flags = DoRed | DoGreen | DoBlue;
c->used = False;
c->invalid = False;
}
if (i >= end)
d = 0;
else
d = MIN(end - i, cnt);
if (i < end)
XQueryColors(dpy, rcmap, xcol, d);
if (!flg) {
for (j = d; j < cnt; j++) {
xcol[j].flags = DoRed | DoGreen | DoBlue;
xcol[j].red = xcol[j].green = xcol[j].blue = 0xffff;
}
if (d == 0)
flg = True;
}
if (!map->isDefault)
XStoreColors(dpy, map->cmap, xcol, cnt);
for (j = 0; j < cnt; j++) {
Col *c = &ctable[i + j];
c->color.red = xcol[j].red & 0xff00;
c->color.green = xcol[j].green & 0xff00;
c->color.blue = xcol[j].blue & 0xff00;
HashAdd(map->htable, HASH(c->color), c);
HashAdd(map->ltable, HASH_PIXEL(c->color.pixel), c);
}
}
map->nfree = visual->map_entries;
if (!map->isDefault) {
Boolean got = False;
got = !map->isMapped;
for (i = 0; i < visual->map_entries; i++) {
if (ctable[i].color.pixel == BlackPixelOfScreen(screen)) {
entryUnlink(map, &ctable[i]);
} else if (ctable[i].color.pixel == WhitePixelOfScreen(screen)) {
entryUnlink(map, &ctable[i]);
} else if (!got) {
map->mine = i;
entryUnlink(map, &ctable[i]);
got = True;
}
}
}
addlist:
new = (PaletteList *) XtMalloc(sizeof(PaletteList));
new->dpy = XtDisplay(w);
new->cmap = map->cmap;
new->next = cmapList;
new->map = map;
cmapList = new;
return map;
}
Palette *
PaletteCreate(Widget w)
{
return paletteNew(GetShell(w), False);
}
Palette *
PaletteGetDefault(Widget w)
{
static Palette *defMap = NULL;
if (defMap == NULL)
defMap = paletteNew(GetShell(w), True);
return defMap;
}
static Palette *
paletteGetBW(void)
{
static Palette *map = NULL;
Col *ctable;
if (map != NULL)
return map;
map = XtNew(Palette);
map->display = NULL;
map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
map->list = NULL;
map->last = NULL;
map->isMapped = True;
map->isGrey = True;
map->ncolors = 2;
map->nfree = 0;
map->ctable = NULL;
map->visual = None; /* X */
map->depth = 1;
map->userList = NULL;
map->isDefault = False;
map->mine = None;
map->cmap = None;
ctable = (Col *) XtCalloc(sizeof(Col), map->ncolors);
map->ctable = ctable;
ctable[0].color.pixel = 0;
ctable[0].color.flags = DoRed | DoGreen | DoBlue;
ctable[0].color.red = 0xffff;
ctable[0].color.green = 0xffff;
ctable[0].color.blue = 0xffff;
ctable[0].used = True;
ctable[0].invalid = False;
ctable[1].color.pixel = 1;
ctable[1].color.flags = DoRed | DoGreen | DoBlue;
ctable[1].color.red = 0x0000;
ctable[1].color.green = 0x0000;
ctable[1].color.blue = 0x0000;
ctable[1].used = True;
ctable[1].invalid = False;
HashAdd(map->htable, HASH(ctable[0].color), &ctable[0]);
HashAdd(map->htable, HASH(ctable[1].color), &ctable[1]);
HashAdd(map->ltable, HASH_PIXEL(ctable[0].color.pixel), &ctable[0]);
HashAdd(map->ltable, HASH_PIXEL(ctable[1].color.pixel), &ctable[1]);
return map;
}
static void
addColor(Palette * map, XColor * color)
{
Col *node, *n, *cptr;
int i;
#ifndef VMS
if (map->readonly) {
#else
if (map->Readonly) {
#endif
/*
** The temporary color is needed since alloc
** will change the pixel values.
*/
XColor newc;
newc = *color;
node = XtNew(Col);
newc.flags = DoRed | DoGreen | DoBlue;
XAllocColor(map->display, map->cmap, &newc);
node->color.pixel = color->pixel = newc.pixel;
} else {
if (map->nfree == 0) {
unsigned int d, curDif = ~0;
int rd, gd, bd;
/*
** All free colors used up -- use closest match.
*/
for (i = 0, cptr = (Col *) map->ctable; i < map->ncolors; i++, cptr++) {
rd = (cptr->color.red >> 8) - (color->red >> 8);
gd = (cptr->color.green >> 8) - (color->green >> 8);
bd = (cptr->color.blue >> 8) - (color->blue >> 8);
d = rd * rd + gd * gd + bd * bd;
if (d < curDif) {
node = cptr;
curDif = d;
}
}
color->pixel = node->color.pixel;
return;
}
/* Find first free entry -- we know it's there. */
for (i = 0, node = (Col *) map->ctable;
(i < map->ncolors) && node->used; i++, node++);
entryUnlink(map, node); /* mark as used */
color->pixel = node->color.pixel;
n = HashFind(map->ltable, HASH_PIXEL(node->color.pixel), node);
HashRemove(map->ltable, HASH_PIXEL(node->color.pixel), n);
HashRemove(map->htable, HASH(*color), node);
}
node->used = True;
node->invalid = False;
node->color.red = color->red & 0xff00;
node->color.green = color->green & 0xff00;
node->color.blue = color->blue & 0xff00;
node->color.flags = DoRed | DoGreen | DoBlue;
#ifndef VMS
if (!map->readonly)
#else
if (!map->Readonly)
#endif
XStoreColor(map->display, map->cmap, &node->color);
HashAdd(map->htable, HASH(*color), node);
HashAdd(map->ltable, HASH_PIXEL(color->pixel), node);
}
/*
* Try to allocate the specified XColor in the colormap.
* If the color is already in the palette, use that instead.
* Return the corresponding Pixel value in 'list'.
*/
static Col *
allocN(Palette * map, XColor * color, Pixel * list)
{
Col c, *node;
c.color.red = color->red & 0xff00;
c.color.green = color->green & 0xff00;
c.color.blue = color->blue & 0xff00;
if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
addColor(map, color);
*list = color->pixel;
} else {
/*
** It must have been allocated in the previous
** pass, or by the above.
*/
if (!node->used)
entryUnlink(map, node);
*list = node->color.pixel;
}
return node;
}
int
PaletteAllocN(Palette * map, XColor * color, int ncolor, Pixel * list)
{
Boolean *flg = XtCalloc(sizeof(Boolean), ncolor);
Col c, *node;
int i;
Boolean newMine = False;
if (!map->isMapped) {
for (i = 0; i < ncolor; i++) {
unsigned int r, g, b;
r = (color[i].red * map->rRange) >> 16;
g = (color[i].green * map->gRange) >> 16;
b = (color[i].blue * map->bRange) >> 16;
list[i] = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
}
return 0;
}
for (i = 0; i < ncolor; i++) {
Col c;
c.color.red = color[i].red & 0xff00;
c.color.green = color[i].green & 0xff00;
c.color.blue = color[i].blue & 0xff00;
if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
flg[i] = True;
/*
** Match found, if the entry hasn't been "alloced"
** yet, mark it so, and remove it from the "free" list.
*/
if (!node->used)
entryUnlink(map, node);
list[i] = node->color.pixel;
if (list[i] == map->mine)
newMine = True;
} else {
flg[i] = False;
}
}
for (i = 0; i < ncolor; i++) {
if (flg[i])
continue;
c.color.red = color[i].red & 0xff00;
c.color.green = color[i].green & 0xff00;
c.color.blue = color[i].blue & 0xff00;
if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
addColor(map, &color[i]);
list[i] = color[i].pixel;
} else {
/*
** It must have been allocated in the previous
** pass, or by the above.
*/
list[i] = node->color.pixel;
}
if (list[i] == map->mine)
newMine = True;
}
XtFree((XtPointer) flg);
if (newMine && map->ctable != NULL) {
Col *cptr = (Col *) map->ctable;
for (i = 0; i < map->ncolors; i++, cptr++) {
if (cptr->used)
continue;
map->mine = cptr->color.pixel;
break;
}
}
return 0;
}
/*
* Return the Pixel corresponding to (or closest to) the RGB
* values specified in 'color'.
* If no match is found in the existing palette, try to allocate
* a new color.
*/
Pixel
PaletteAlloc(Palette * map, XColor * color)
{
if (!map->isMapped) {
unsigned int r, g, b;
r = (color->red * map->rRange) >> 16;
g = (color->green * map->gRange) >> 16;
b = (color->blue * map->bRange) >> 16;
return (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
}
if (map->last != NULL) {
XColor *lc = (XColor *) map->last;
if (lc->red == color->red &&
lc->green == color->green &&
lc->blue == color->blue)
return color->pixel = lc->pixel;
}
map->last = allocN(map, color, &color->pixel);
return color->pixel;
}
/*
** Given a Pixel value on the specified map return the
** RGB value.
** On non-TrueColor displays, the returned value is actually a pointer
** into map->ctable[].
**
** Special case "TrueColor" since it is just computed.
*/
XColor *
PaletteLookup(Palette * map, Pixel pix)
{
if (map->isMapped) {
Col col;
Col *c;
col.color.pixel = pix;
if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL) {
printf("Shouldn't happen\n");
return NULL;
}
if (c->invalid) {
HashRemove(map->htable, HASH(c->color), c);
XQueryColor(map->display, map->cmap, &c->color);
c->color.red &= 0xff00;
c->color.green &= 0xff00;
c->color.blue &= 0xff00;
HashAdd(map->htable, HASH(c->color), c);
c->invalid = False;
}
return &c->color;
} else {
static XColor xc;
xc.red = (pix >> map->rShift) & (map->rRange - 1);
xc.green = (pix >> map->gShift) & (map->gRange - 1);
xc.blue = (pix >> map->bShift) & (map->bRange - 1);
xc.red *= 65536 / map->rRange;
xc.green *= 65536 / map->gRange;
xc.blue *= 65536 / map->bRange;
return &xc;
}
}
Boolean
PaletteLookupColor(Palette * map, XColor * col, Pixel * pxl)
{
Col c, *node;
if (!map->isMapped) {
unsigned int r, g, b;
r = (col->red * map->rRange) >> 16;
g = (col->green * map->gRange) >> 16;
b = (col->blue * map->bRange) >> 16;
*pxl = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
return True;
}
c.color.red = col->red & 0xff00;
c.color.green = col->green & 0xff00;
c.color.blue = col->blue & 0xff00;
if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
*pxl = node->color.pixel;
return True;
}
return False;
}
Pixel
PaletteGetUnused(Palette * map)
{
return map->mine;
}
/*
** Change the RGB value of a pixel
*/
void
PaletteSetInvalid(Palette * map, Pixel pix)
{
Col col, *c;
#ifndef VMS
if (!map->isMapped || map->readonly)
#else
if (!map->isMapped || map->Readonly)
#endif
return;
col.color.pixel = pix;
if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL)
return;
c->invalid = True;
}
/*
** This will fail if on a read only colormap
*/
Boolean
PaletteSetPixel(Palette * map, Pixel pixel, XColor * xcol)
{
Col col, *c;
#ifndef VMS
if (map->readonly)
#else
if (map->Readonly)
#endif
return False;
if (map->isDefault)
return False;
xcol->pixel = pixel;
xcol->flags = DoRed | DoGreen | DoBlue;
XStoreColor(map->display, map->cmap, xcol);
col.color.pixel = pixel;
if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pixel), &col)) != NULL)
c->invalid = True;
return True;
}
Palette *
PaletteFindDpy(Display * dpy, Colormap cmap)
{
PaletteList *cur;
if (cmap == -1)
return paletteGetBW();
for (cur = cmapList; cur != NULL; cur = cur->next)
if (cur->cmap == cmap && cur->dpy == dpy)
return cur->map;
return NULL;
}
Palette *
PaletteFind(Widget w, Colormap cmap)
{
return PaletteFindDpy(XtDisplay(w), cmap);
}
static void
paletteAddUserDestroy(Widget w, Palette * map, XtPointer junk)
{
paletteUserList *cur = map->userList;
paletteUserList **prev = &map->userList;
while (cur != NULL && cur->widget != w) {
prev = &cur->next;
cur = cur->next;
}
if (cur == NULL)
return;
*prev = cur->next;
XtFree((XtPointer) cur);
}
void
PaletteDelete(Palette * map)
{
PaletteList *cur, **prev;
paletteUserList *ul, *nx;
HashDestroy(map->htable);
HashDestroy(map->ltable);
if (map->ctable != NULL)
XtFree((XtPointer) map->ctable);
for (cur = cmapList, prev = &cmapList; cur != NULL && cur->map != map;
prev = &cur->next, cur = cur->next);
if (cur != NULL)
*prev = cur->next;
ul = map->userList;
while (ul != NULL) {
nx = ul->next;
XtRemoveCallback(ul->widget, XtNdestroyCallback,
(XtCallbackProc) paletteAddUserDestroy,
(XtPointer) map);
XtFree((XtPointer) ul);
ul = nx;
}
XtFree((XtPointer) map);
XtFree((XtPointer) cur);
}
void
PaletteAddUser(Palette * map, Widget w)
{
paletteUserList *n = XtNew(paletteUserList);
n->widget = w;
n->next = map->userList;
map->userList = n;
XtAddCallback(w, XtNdestroyCallback,
(XtCallbackProc) paletteAddUserDestroy, (XtPointer) map);
}