1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com) | */
3 /* | | */
4 /* | Permission to use, copy, modify, and to distribute this software | */
5 /* | and its documentation for any purpose is hereby granted without | */
6 /* | fee, provided that the above copyright notice appear in all | */
7 /* | copies and that both that copyright notice and this permission | */
8 /* | notice appear in supporting documentation. There is no | */
9 /* | representations about the suitability of this software for | */
10 /* | any purpose. this software is provided "as is" without express | */
11 /* | or implied warranty. | */
12 /* | | */
13 /* +-------------------------------------------------------------------+ */
14
15 /* $Id: palette.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
16
17 #ifdef __VMS
18 #define XtDisplay XTDISPLAY
19 #define XtScreen XTSCREEN
20 #endif
21
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 #include <X11/Shell.h>
25 #include <X11/Xatom.h>
26 #include <stdio.h>
27 #include "palette.h"
28 #include "hash.h"
29 #include "messages.h"
30 #include "misc.h"
31 #include "image.h"
32 #include "xpaint.h"
33
34 typedef struct list_s {
35 Display *dpy;
36 Colormap cmap;
37 Palette *map;
38 struct list_s *next;
39 } PaletteList;
40
41 typedef struct Col_s {
42 XColor color;
43 Boolean used;
44 Boolean invalid;
45 } Col;
46
47 #define HASH_SIZE 128
48 #define HASH(c) ((int)((c).red + (c).green + (c).blue) % HASH_SIZE)
49 #define HASH_PIXEL(c) ((c) % HASH_SIZE)
50
51 static PaletteList *cmapList = NULL;
52
53 static int
readCMP(void * cca,void * ccb)54 readCMP(void *cca, void *ccb)
55 {
56 XColor *ca = &((Col *) cca)->color, *cb = &((Col *) ccb)->color;
57
58 if (ca->red != cb->red)
59 return ca->red < cb->red ? -1 : 1;
60 if (ca->green != cb->green)
61 return ca->green < cb->green ? -1 : 1;
62 if (ca->blue != cb->blue)
63 return ca->blue < cb->blue ? -1 : 1;
64 return 0;
65 }
66
67 static int
cmpPixel(void * cca,void * ccb)68 cmpPixel(void *cca, void *ccb)
69 {
70 if (((Col *) cca)->color.pixel == ((Col *) ccb)->color.pixel)
71 return 0;
72 return (((Col *) cca)->color.pixel < ((Col *) ccb)->color.pixel) ? -1 : 1;
73 }
74
75 static void
freeFunc(void * junk)76 freeFunc(void *junk)
77 {
78 /*
79 ** Nop free func
80 */
81 }
82
83 static void
entryUnlink(Palette * map,Col * node)84 entryUnlink(Palette * map, Col * node)
85 {
86 node->used = True;
87 node->invalid = False;
88 map->nfree--;
89 }
90
91
92 #define STEP 256
93
94 static Palette *
paletteNew(Widget w,Boolean useDefault)95 paletteNew(Widget w, Boolean useDefault)
96 {
97 int i, v;
98 Display *dpy = XtDisplay(w);
99 Screen *screen = XtScreen(w);
100 Colormap rcmap = DefaultColormapOfScreen(screen);
101 Palette *map = XtNew(Palette);
102 Col *ctable;
103 Visual *visual = NULL;
104 PaletteList *new;
105 int end, depth = -1;
106 Boolean flg = False;
107
108 PopdownMenusGlobal();
109
110 XtVaGetValues(w, XtNvisual, &visual, XtNdepth, &depth, NULL);
111 if (visual == NULL || useDefault)
112 visual = DefaultVisualOfScreen(screen);
113 if (depth <= 0 || useDefault)
114 depth = DefaultDepthOfScreen(screen);
115
116 map->htable = NULL;
117 map->ltable = NULL;
118 map->display = dpy;
119 map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
120 map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
121 map->list = NULL;
122 map->last = NULL;
123 map->isMapped = visual->class != TrueColor;
124 map->isGrey = (visual->class == StaticGray || visual->class == GrayScale);
125 map->ncolors = visual->map_entries;
126 map->nfree = 0;
127 map->ctable = NULL;
128 map->visual = visual;
129 map->depth = depth;
130 map->userList = NULL;
131 map->isDefault = False;
132 map->mine = None;
133
134 switch (visual->class) {
135 case TrueColor:
136 map->rShift = 0;
137 map->gShift = 0;
138 map->bShift = 0;
139 map->rRange = 1;
140 map->gRange = 1;
141 map->bRange = 1;
142 for (v = visual->red_mask; (v & 1) == 0; v >>= 1)
143 map->rShift++;
144 for (; (v & 1) == 1; v >>= 1)
145 map->rRange <<= 1;
146 for (v = visual->green_mask; (v & 1) == 0; v >>= 1)
147 map->gShift++;
148 for (; (v & 1) == 1; v >>= 1)
149 map->gRange <<= 1;
150 for (v = visual->blue_mask; (v & 1) == 0; v >>= 1)
151 map->bShift++;
152 for (; (v & 1) == 1; v >>= 1)
153 map->bRange <<= 1;
154 case StaticGray:
155 case StaticColor:
156 map->readonly_ = True;
157 map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
158 visual, AllocNone);
159 map->isDefault = False;
160 goto addlist;
161 default:
162 map->readonly_ = False;
163 if (useDefault) {
164 map->cmap = rcmap;
165 map->isDefault = True;
166 } else {
167 map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
168 visual, AllocAll);
169 }
170 break;
171 }
172
173 ctable = (Col *) XtCalloc(sizeof(Col), visual->map_entries);
174 map->ctable = ctable;
175 end = CellsOfScreen(screen);
176 for (i = 0; i < visual->map_entries; i += STEP) {
177 XColor xcol[STEP];
178 int cnt = visual->map_entries - i;
179 int j, d;
180
181 if (cnt > STEP)
182 cnt = STEP;
183
184 for (j = 0; j < cnt; j++) {
185 Col *c = &ctable[i + j];
186
187 c->color.pixel = i + j;
188 c->color.flags = DoRed | DoGreen | DoBlue;
189 xcol[j].pixel = i + j;
190 xcol[j].flags = DoRed | DoGreen | DoBlue;
191
192 c->used = False;
193 c->invalid = False;
194 }
195
196 if (i >= end)
197 d = 0;
198 else
199 d = MIN(end - i, cnt);
200
201 if (i < end)
202 XQueryColors(dpy, rcmap, xcol, d);
203 if (!flg) {
204 for (j = d; j < cnt; j++) {
205 xcol[j].flags = DoRed | DoGreen | DoBlue;
206 xcol[j].red = xcol[j].green = xcol[j].blue = 0xffff;
207 }
208 if (d == 0)
209 flg = True;
210 }
211 if (!map->isDefault)
212 XStoreColors(dpy, map->cmap, xcol, cnt);
213
214 for (j = 0; j < cnt; j++) {
215 Col *c = &ctable[i + j];
216
217 c->color.red = xcol[j].red & 0xff00;
218 c->color.green = xcol[j].green & 0xff00;
219 c->color.blue = xcol[j].blue & 0xff00;
220 HashAdd(map->htable, HASH(c->color), c);
221 HashAdd(map->ltable, HASH_PIXEL(c->color.pixel), c);
222 }
223 }
224
225 map->nfree = visual->map_entries;
226
227 if (!map->isDefault) {
228 Boolean got = False;
229
230 got = !map->isMapped;
231 for (i = 0; i < visual->map_entries; i++) {
232 if (ctable[i].color.pixel == BlackPixelOfScreen(screen)) {
233 entryUnlink(map, &ctable[i]);
234 } else if (ctable[i].color.pixel == WhitePixelOfScreen(screen)) {
235 entryUnlink(map, &ctable[i]);
236 } else if (!got) {
237 map->mine = i;
238 entryUnlink(map, &ctable[i]);
239 got = True;
240 }
241 }
242 }
243 addlist:
244 new = (PaletteList *) XtMalloc(sizeof(PaletteList));
245 new->dpy = XtDisplay(w);
246 new->cmap = map->cmap;
247 new->next = cmapList;
248 new->map = map;
249 cmapList = new;
250
251 return map;
252 }
253
254 Palette *
PaletteCreate(Widget w)255 PaletteCreate(Widget w)
256 {
257 return paletteNew(GetShell(w), False);
258 }
259
260 Palette *
PaletteGetDefault(Widget w)261 PaletteGetDefault(Widget w)
262 {
263 static Palette *defMap = NULL;
264
265 if (defMap == NULL)
266 defMap = paletteNew(GetShell(w), True);
267
268 return defMap;
269 }
270
271 static Palette *
paletteGetBW(void)272 paletteGetBW(void)
273 {
274 static Palette *map = NULL;
275 Col *ctable;
276
277 if (map != NULL)
278 return map;
279
280 map = XtNew(Palette);
281
282 map->display = NULL;
283 map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
284 map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
285 map->list = NULL;
286 map->last = NULL;
287 map->isMapped = True;
288 map->isGrey = True;
289 map->ncolors = 2;
290 map->nfree = 0;
291 map->ctable = NULL;
292 map->visual = None; /* X */
293 map->depth = 1;
294 map->userList = NULL;
295 map->isDefault = False;
296 map->mine = None;
297 map->cmap = None;
298
299 ctable = (Col *) XtCalloc(sizeof(Col), map->ncolors);
300 map->ctable = ctable;
301
302 ctable[0].color.pixel = 0;
303 ctable[0].color.flags = DoRed | DoGreen | DoBlue;
304 ctable[0].color.red = 0xffff;
305 ctable[0].color.green = 0xffff;
306 ctable[0].color.blue = 0xffff;
307 ctable[0].used = True;
308 ctable[0].invalid = False;
309
310 ctable[1].color.pixel = 1;
311 ctable[1].color.flags = DoRed | DoGreen | DoBlue;
312 ctable[1].color.red = 0x0000;
313 ctable[1].color.green = 0x0000;
314 ctable[1].color.blue = 0x0000;
315 ctable[1].used = True;
316 ctable[1].invalid = False;
317
318 HashAdd(map->htable, HASH(ctable[0].color), &ctable[0]);
319 HashAdd(map->htable, HASH(ctable[1].color), &ctable[1]);
320 HashAdd(map->ltable, HASH_PIXEL(ctable[0].color.pixel), &ctable[0]);
321 HashAdd(map->ltable, HASH_PIXEL(ctable[1].color.pixel), &ctable[1]);
322
323 return map;
324 }
325
326 static void
addColor(Palette * map,XColor * color)327 addColor(Palette * map, XColor * color)
328 {
329 Col *node = NULL, *n, *cptr;
330 int i;
331
332
333 if (map->readonly_) {
334 /*
335 ** The temporary color is needed since alloc
336 ** will change the pixel values.
337 */
338 XColor newc;
339 newc = *color;
340 node = XtNew(Col);
341
342 newc.flags = DoRed | DoGreen | DoBlue;
343 XAllocColor(map->display, map->cmap, &newc);
344 node->color.pixel = color->pixel = newc.pixel;
345 } else {
346 if (map->nfree == 0) {
347 unsigned int d, curDif = ~0;
348 int rd, gd, bd;
349 /*
350 ** All free colors used up -- use closest match.
351 */
352 for (i = 0, cptr = (Col *) map->ctable; i < map->ncolors; i++, cptr++) {
353 rd = (cptr->color.red >> 8) - (color->red >> 8);
354 gd = (cptr->color.green >> 8) - (color->green >> 8);
355 bd = (cptr->color.blue >> 8) - (color->blue >> 8);
356 d = rd * rd + gd * gd + bd * bd;
357 if (d < curDif) {
358 node = cptr;
359 curDif = d;
360 }
361 }
362 color->pixel = node->color.pixel;
363 return;
364 }
365 /* Find first free entry -- we know it's there. */
366 for (i = 0, node = (Col *) map->ctable;
367 (i < map->ncolors) && node->used; i++, node++);
368 entryUnlink(map, node); /* mark as used */
369
370 color->pixel = node->color.pixel;
371 n = HashFind(map->ltable, HASH_PIXEL(node->color.pixel), node);
372 HashRemove(map->ltable, HASH_PIXEL(node->color.pixel), n);
373 HashRemove(map->htable, HASH(*color), node);
374 }
375
376 node->used = True;
377 node->invalid = False;
378 node->color.red = color->red & 0xff00;
379 node->color.green = color->green & 0xff00;
380 node->color.blue = color->blue & 0xff00;
381 node->color.flags = DoRed | DoGreen | DoBlue;
382 if (!map->readonly_)
383 XStoreColor(map->display, map->cmap, &node->color);
384 HashAdd(map->htable, HASH(*color), node);
385 HashAdd(map->ltable, HASH_PIXEL(color->pixel), node);
386 }
387
388 /*
389 * Try to allocate the specified XColor in the colormap.
390 * If the color is already in the palette, use that instead.
391 * Return the corresponding Pixel value in 'list'.
392 */
393 static Col *
allocN(Palette * map,XColor * color,Pixel * list)394 allocN(Palette * map, XColor * color, Pixel * list)
395 {
396 Col c, *node;
397
398 c.color.red = color->red & 0xff00;
399 c.color.green = color->green & 0xff00;
400 c.color.blue = color->blue & 0xff00;
401
402 if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
403 addColor(map, color);
404 *list = color->pixel;
405 } else {
406 /*
407 ** It must have been allocated in the previous
408 ** pass, or by the above.
409 */
410 if (!node->used)
411 entryUnlink(map, node);
412 *list = node->color.pixel;
413 }
414
415 return node;
416 }
417
418 int
PaletteAllocN(Palette * map,XColor * color,int ncolor,Pixel * list)419 PaletteAllocN(Palette * map, XColor * color, int ncolor, Pixel * list)
420 {
421 Boolean *flg = XtCalloc(sizeof(Boolean), ncolor);
422 Col c, *node;
423 int i;
424 Boolean newMine = False;
425
426 if (!map->isMapped) {
427 for (i = 0; i < ncolor; i++) {
428 unsigned int r, g, b;
429
430 r = (color[i].red * map->rRange) >> 16;
431 g = (color[i].green * map->gRange) >> 16;
432 b = (color[i].blue * map->bRange) >> 16;
433 list[i] = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
434 }
435 return 0;
436 }
437 for (i = 0; i < ncolor; i++) {
438 Col c;
439
440 c.color.red = color[i].red & 0xff00;
441 c.color.green = color[i].green & 0xff00;
442 c.color.blue = color[i].blue & 0xff00;
443
444 if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
445 flg[i] = True;
446 /*
447 ** Match found, if the entry hasn't been "alloced"
448 ** yet, mark it so, and remove it from the "free" list.
449 */
450 if (!node->used)
451 entryUnlink(map, node);
452 list[i] = node->color.pixel;
453 if (list[i] == map->mine)
454 newMine = True;
455 } else {
456 flg[i] = False;
457 }
458 }
459
460 for (i = 0; i < ncolor; i++) {
461 if (flg[i])
462 continue;
463
464 c.color.red = color[i].red & 0xff00;
465 c.color.green = color[i].green & 0xff00;
466 c.color.blue = color[i].blue & 0xff00;
467
468 if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
469 addColor(map, &color[i]);
470 list[i] = color[i].pixel;
471 } else {
472 /*
473 ** It must have been allocated in the previous
474 ** pass, or by the above.
475 */
476 list[i] = node->color.pixel;
477 }
478 if (list[i] == map->mine)
479 newMine = True;
480 }
481
482 XtFree((XtPointer) flg);
483
484 if (newMine && map->ctable != NULL) {
485 Col *cptr = (Col *) map->ctable;
486
487 for (i = 0; i < map->ncolors; i++, cptr++) {
488 if (cptr->used)
489 continue;
490 map->mine = cptr->color.pixel;
491 break;
492 }
493 }
494 return 0;
495 }
496
497 /*
498 * Return the Pixel corresponding to (or closest to) the RGB
499 * values specified in 'color'.
500 * If no match is found in the existing palette, try to allocate
501 * a new color.
502 */
503 Pixel
PaletteAlloc(Palette * map,XColor * color)504 PaletteAlloc(Palette * map, XColor * color)
505 {
506 if (!map->isMapped) {
507 unsigned int r, g, b;
508
509 r = (color->red * map->rRange) >> 16;
510 g = (color->green * map->gRange) >> 16;
511 b = (color->blue * map->bRange) >> 16;
512 return (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
513 }
514 if (map->last != NULL) {
515 XColor *lc = (XColor *) map->last;
516
517 if (lc->red == color->red &&
518 lc->green == color->green &&
519 lc->blue == color->blue)
520 return color->pixel = lc->pixel;
521 }
522 map->last = allocN(map, color, &color->pixel);
523
524 return color->pixel;
525 }
526
527 /*
528 ** Given a Pixel value on the specified map return the
529 ** RGB value.
530 ** On non-TrueColor displays, the returned value is actually a pointer
531 ** into map->ctable[].
532 **
533 ** Special case "TrueColor" since it is just computed.
534 */
535 XColor *
PaletteLookup(Palette * map,Pixel pix)536 PaletteLookup(Palette * map, Pixel pix)
537 {
538 if (map->isMapped) {
539 Col col;
540 Col *c;
541 col.color.pixel = pix;
542
543 if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL) {
544 fprintf(stderr,"%s", msgText[SHOULDNT_HAPPEN]);
545 return NULL;
546 }
547 if (c->invalid) {
548 HashRemove(map->htable, HASH(c->color), c);
549 XQueryColor(map->display, map->cmap, &c->color);
550 c->color.red &= 0xff00;
551 c->color.green &= 0xff00;
552 c->color.blue &= 0xff00;
553 HashAdd(map->htable, HASH(c->color), c);
554 c->invalid = False;
555 }
556 return &c->color;
557 } else {
558 static XColor xc;
559
560 xc.red = (pix >> map->rShift) & (map->rRange - 1);
561 xc.green = (pix >> map->gShift) & (map->gRange - 1);
562 xc.blue = (pix >> map->bShift) & (map->bRange - 1);
563
564 xc.red *= 65536 / map->rRange;
565 xc.green *= 65536 / map->gRange;
566 xc.blue *= 65536 / map->bRange;
567
568 return &xc;
569 }
570 }
571
572 Boolean
PaletteLookupColor(Palette * map,XColor * col,Pixel * pxl)573 PaletteLookupColor(Palette * map, XColor * col, Pixel * pxl)
574 {
575 Col c, *node;
576
577 if (!map->isMapped) {
578 unsigned int r, g, b;
579
580 r = (col->red * map->rRange) >> 16;
581 g = (col->green * map->gRange) >> 16;
582 b = (col->blue * map->bRange) >> 16;
583 *pxl = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
584 return True;
585 }
586 c.color.red = col->red & 0xff00;
587 c.color.green = col->green & 0xff00;
588 c.color.blue = col->blue & 0xff00;
589
590 if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
591 *pxl = node->color.pixel;
592 return True;
593 }
594 return False;
595 }
596
597 Pixel
PaletteGetUnused(Palette * map)598 PaletteGetUnused(Palette * map)
599 {
600 return map->mine;
601 }
602
603 /*
604 ** Change the RGB value of a pixel
605 */
606 void
PaletteSetInvalid(Palette * map,Pixel pix)607 PaletteSetInvalid(Palette * map, Pixel pix)
608 {
609 Col col, *c;
610
611 if (!map->isMapped || map->readonly_)
612 return;
613
614 col.color.pixel = pix;
615
616 if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL)
617 return;
618
619 c->invalid = True;
620 }
621
622 /*
623 ** This will fail if on a read only colormap
624 */
625 Boolean
PaletteSetPixel(Palette * map,Pixel pixel,XColor * xcol)626 PaletteSetPixel(Palette * map, Pixel pixel, XColor * xcol)
627 {
628 Col col, *c;
629
630 if (map->readonly_)
631 return False;
632
633 if (map->isDefault)
634 return False;
635
636 xcol->pixel = pixel;
637 xcol->flags = DoRed | DoGreen | DoBlue;
638 XStoreColor(map->display, map->cmap, xcol);
639
640 col.color.pixel = pixel;
641
642 if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pixel), &col)) != NULL)
643 c->invalid = True;
644
645 return True;
646 }
647
648 Palette *
PaletteFindDpy(Display * dpy,Colormap cmap)649 PaletteFindDpy(Display * dpy, Colormap cmap)
650 {
651 PaletteList *cur;
652
653 if (cmap == -1)
654 return paletteGetBW();
655
656 for (cur = cmapList; cur != NULL; cur = cur->next)
657 if (cur->cmap == cmap && cur->dpy == dpy)
658 return cur->map;
659
660 return NULL;
661 }
662
663 Palette *
PaletteFind(Widget w,Colormap cmap)664 PaletteFind(Widget w, Colormap cmap)
665 {
666 return PaletteFindDpy(XtDisplay(w), cmap);
667 }
668
669 static void
paletteAddUserDestroy(Widget w,Palette * map,XtPointer junk)670 paletteAddUserDestroy(Widget w, Palette * map, XtPointer junk)
671 {
672 paletteUserList *cur = map->userList;
673 paletteUserList **prev = &map->userList;
674
675 while (cur != NULL && cur->widget != w) {
676 prev = &cur->next;
677 cur = cur->next;
678 }
679
680 if (cur == NULL)
681 return;
682
683 *prev = cur->next;
684 XtFree((XtPointer) cur);
685 }
686
687 void
PaletteDelete(Palette * map)688 PaletteDelete(Palette * map)
689 {
690 PaletteList *cur, **prev;
691 paletteUserList *ul, *nx;
692
693 HashDestroy(map->htable);
694 HashDestroy(map->ltable);
695 if (map->ctable != NULL)
696 XtFree((XtPointer) map->ctable);
697
698 for (cur = cmapList, prev = &cmapList; cur != NULL && cur->map != map;
699 prev = &cur->next, cur = cur->next);
700
701 if (cur != NULL)
702 *prev = cur->next;
703
704 ul = map->userList;
705 while (ul != NULL) {
706 nx = ul->next;
707 XtRemoveCallback(ul->widget, XtNdestroyCallback,
708 (XtCallbackProc) paletteAddUserDestroy,
709 (XtPointer) map);
710 XtFree((XtPointer) ul);
711 ul = nx;
712 }
713
714 XtFree((XtPointer) map);
715 XtFree((XtPointer) cur);
716 }
717
718 void
PaletteAddUser(Palette * map,Widget w)719 PaletteAddUser(Palette * map, Widget w)
720 {
721 paletteUserList *n = XtNew(paletteUserList);
722
723 n->widget = w;
724 n->next = map->userList;
725 map->userList = n;
726 XtAddCallback(w, XtNdestroyCallback,
727 (XtCallbackProc) paletteAddUserDestroy, (XtPointer) map);
728 }
729