1 /*
2  * $Id: pals.c,v 1.1 2005-09-18 22:05:34 dhmunro Exp $
3  * palette handling for X11
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "config.h"
12 #include "playx.h"
13 #include "pstdlib.h"
14 
15 static int x_use_shared(p_scr *s, p_col_t color, p_col_t *pixel);
16 static void x_lose_shared(p_scr *s, p_col_t *pixels, int n);
17 static void x_all_shared(p_scr *s, XColor *map /*[256]*/);
18 static p_col_t x_best_shared(p_scr *s, p_col_t color,
19                              XColor *map, int n);
20 static void x_cavailable(void *p, p_hashkey key, void *ctx);
21 static void x_list_dead(void *p, p_hashkey key, void *ctx);
22 static void x_find_dead(void *p, p_hashkey key, void *ctx);
23 static void x_mark_shared(void *p);
24 
25 void
p_palette(p_win * w,p_col_t * colors,int n)26 p_palette(p_win *w, p_col_t *colors, int n)
27 {
28   p_scr *s = w->s;
29   p_col_t r, g, b;
30   int i;
31   if (n>240) n = 240;
32 
33   if (w->parent) w = w->parent;  /* pixmap uses parent palette */
34   s->gc_color = -1;
35 
36   if (s->vclass==TrueColor || s->vclass==DirectColor) {
37     for (i=0 ; i<n ; i++) {
38       r = P_R(colors[i]);
39       g = P_G(colors[i]);
40       b = P_B(colors[i]);
41       w->pixels[i] = (s->pixels[r]&s->rmask) |
42         (s->pixels[g]&s->gmask) | (s->pixels[b]&s->bmask);
43     }
44     w->n_palette = n;
45 
46   } else if (s->vclass!=PseudoColor) {
47     for (i=0 ; i<n ; i++) {
48       r = P_R(colors[i]);
49       g = P_G(colors[i]);
50       b = P_B(colors[i]);
51       w->pixels[i] = s->pixels[(r+g+b)/3];
52     }
53     w->n_palette = n;
54 
55   } else if (w->rgb_pixels) {
56     for (i=0 ; i<n ; i++) {
57       r = P_R(colors[i]);
58       g = P_G(colors[i]);
59       b = P_B(colors[i]);
60       r = (r+32)>>6;
61       g = (g+16)>>5;
62       b = (b+32)>>6;
63       g += b+(b<<3);
64       w->pixels[i] = w->rgb_pixels[r+g+(g<<2)];  /* r + 5*g * 45*b */
65     }
66     w->n_palette = n;
67 
68   } else {
69     XColor c;
70     int p;
71     p_col_t fg_pixel = s->colors[1].pixel;
72     Display *dpy = s->xdpy->dpy;
73     Visual *visual = DefaultVisual(dpy,s->scr_num);
74     int map_size = visual->map_entries;
75     if (map_size>256) map_size = 256;
76 
77     if (w->cmap==None) {
78       /* this window uses default colormap (read-only shared colors) */
79       int n_old = w->n_palette;
80 
81       /* (1) free our use of existing colors */
82       w->n_palette = 0;
83       x_lose_shared(s, w->pixels, n_old);
84       for (i=0 ; i<n_old ; i++) w->pixels[i] = fg_pixel;
85       if (n<=0) return;
86 
87       /* (2) go for it, but prudently ask in bit reversed order */
88       for (i=0 ; i<256 ; i++) {
89         p = p_bit_rev[i];
90         if (p>=n) continue;
91         if (!x_use_shared(s, colors[p], &w->pixels[p])) break;
92         w->n_palette++;
93       }
94 
95       /* (3) fallback if didn't get full request */
96       if (w->n_palette<n) {
97         XColor map[256];
98         int nsh;
99 
100         /* query all colors, reserving a use of all sharable colors */
101         x_all_shared(s, map);
102         for (nsh=0 ; nsh<256 ; nsh++) {
103           if (map[nsh].flags) break;
104           map[nsh].red =   (map[nsh].red  >>8)&0xff;
105           map[nsh].green = (map[nsh].green>>8)&0xff;
106           map[nsh].blue =  (map[nsh].blue >>8)&0xff;
107         }
108 
109         /* take closest sharable pixel to those requested
110          * - allocate extra uses for colors used more than once */
111         for (; i<256 ; i++) {
112           p = p_bit_rev[i];
113           if (p>=n) continue;
114           w->pixels[p] = x_best_shared(s, colors[p], map, nsh);
115           w->n_palette++;
116         }
117 
118         /* release shared colors we didn't ever use */
119         x_lose_shared(s, (p_col_t *)0, 0);
120       }
121 
122     } else {
123       /* this window has a private colormap (read-write private colors) */
124       char used[256];
125 
126       /* private colormaps will flash display when they are installed
127        * take two steps to minimize the annoyance:
128        * (1) be sure to allocate the 16 standard colors
129        *     s->colors[0:15] the same as default cmap on screen
130        *     -- x_color algorithm fails without this,
131        *        done when cmap created
132        * (2) allocate palette colors from top down, since most
133        *     X servers seem to pass out colors from bottom up and
134        *     important permanent apps (window manager) started first
135        *     -- allocate colors at bottom same as in default cmap */
136       for (i=0 ; i<map_size ; i++) used[i] = 0;
137       for (i=0 ; i<16 ; i++)
138         if (s->colors[i].pixel<map_size) used[s->colors[i].pixel] = 1;
139       for (p=map_size-1,i=0 ; p>=0 && i<n ; p--) {
140         if (used[p]) continue;
141         c.pixel = w->pixels[i] = p;
142         c.red = P_R(colors[i]) << 8;
143         c.green = P_G(colors[i]) << 8;
144         c.blue = P_B(colors[i]) << 8;
145         i++;
146         c.flags = DoRed | DoGreen | DoBlue;
147         XStoreColor(dpy, w->cmap, &c);
148       }
149       for (; i<240 ; i++) w->pixels[i] = fg_pixel;
150       if (p>=0) {
151         /* restore as much of the default colormap as possible */
152         Colormap cmap = DefaultColormap(dpy,s->scr_num);
153         XColor map[256];
154         for (i=0 ; i<=p ; i++) map[i].pixel = i;
155         XQueryColors(dpy, cmap, map, p+1);
156         for (i=0 ; i<=p ; i++) {
157           if (used[i]) continue;
158           map[i].flags = DoRed | DoGreen | DoBlue;
159           XStoreColor(dpy, w->cmap, &map[i]);
160         }
161       }
162       w->n_palette = n;
163     }
164   }
165   if (p_signalling) p_abort();
166 }
167 
168 int
x_rgb_palette(p_win * w)169 x_rgb_palette(p_win *w)
170 {
171   if (w->parent) w = w->parent;
172   if (!w->rgb_pixels) {
173     p_scr *s = w->s;
174     p_col_t *pixels;
175     int i;
176     if (s->vclass!=PseudoColor) return 0;
177 
178     /* would be friendlier to translate preexisting
179      * palette, but nuking it is far simpler */
180     p_palette(w, p_595, 225);
181     x_tmpzap(&s->tmp);
182     pixels = s->tmp = p_malloc(sizeof(p_col_t)*256);
183     if (!pixels) return 0;
184     for (i=0 ; i<256 ; i++) pixels[i] = w->pixels[i];
185     s->tmp = 0;
186     w->rgb_pixels = pixels;
187     p_palette(w, (p_col_t *)0, 0);
188   }
189   return 1;
190 }
191 
192 /*------------------------------------------------------------------------*/
193 
194 struct x_cshared {
195   unsigned long *usepxl;   /* [uses,pixel] for each pixel */
196   unsigned long nextpxl;   /* index into next free usepxl pair */
197   p_hashtab *bypixel;      /* returns usepxl index given pixel value */
198   p_hashtab *bycolor;      /* returns usepxl index given color value */
199   /* note: bypixel hash table is unnecessary if we were guaranteed
200    *    that the pixel values are <256 for PseudoColor displays,
201    *    as I assume in a few places (e.g.- x_all_shared below)
202    *    - nevertheless, it ensures that x_use_shared, x_lose_shared
203    *      and x_nuke_shared work for all possible X servers
204    */
205 };
206 
207 struct x_deadpix {
208   unsigned long list[256], keys[256];
209   int n, m;
210   x_cshared *shared;
211 };
212 struct x_c2pix {
213   XColor *map;
214   int n;
215 };
216 
217 static int
x_use_shared(p_scr * s,p_col_t color,p_col_t * pixel)218 x_use_shared(p_scr *s, p_col_t color, p_col_t *pixel)
219 {
220   x_cshared *shared = s->shared;
221   p_hashkey colkey = P_IHASH(color);
222   unsigned long *usepxl;
223 
224   usepxl = p_hfind(shared->bycolor, colkey);
225 
226   if (!usepxl) {
227     Display *dpy = s->xdpy->dpy;
228     Colormap cmap = DefaultColormap(dpy, s->scr_num);
229     p_hashkey pixkey;
230     XColor c;
231     unsigned long nextpxl = shared->nextpxl;
232     if (nextpxl>=512) return 0;      /* only provide for 256 shared colors */
233     c.red = P_R(color) << 8;
234     c.green = P_G(color) << 8;
235     c.blue = P_B(color) << 8;
236     if (!XAllocColor(dpy, cmap, &c)) return 0; /* default colormap is full */
237     pixkey = P_IHASH(c.pixel);
238     usepxl = p_hfind(shared->bypixel, pixkey);
239     if (usepxl) {                  /* different colors may give same pixel */
240       XFreeColors(dpy, cmap, &c.pixel, 1, 0UL);
241       p_hinsert(shared->bycolor, colkey, usepxl);
242     } else {
243       usepxl = shared->usepxl + nextpxl;
244       shared->nextpxl = usepxl[0];
245       usepxl[0] = 0;     /* this will be first use */
246       usepxl[1] = c.pixel;
247       p_hinsert(shared->bypixel, pixkey, usepxl);
248       p_hinsert(shared->bycolor, colkey, usepxl);
249     }
250   }
251 
252   usepxl[0]++;
253   *pixel = usepxl[1];
254   return 1;
255 }
256 
257 static void
x_lose_shared(p_scr * s,p_col_t * pixels,int n)258 x_lose_shared(p_scr *s, p_col_t *pixels, int n)
259 {
260   x_cshared *shared = s->shared;
261   unsigned long *usepxl;
262   struct x_deadpix deadpix;
263   int i;
264 
265   if (!shared) {
266     shared = p_malloc(sizeof(x_cshared));
267     if (!shared) return;
268     shared->bycolor = p_halloc(256);
269     shared->bypixel = p_halloc(256);
270     shared->usepxl = p_malloc(sizeof(unsigned long)*512);
271     if (!shared->bycolor || !shared->usepxl) return;
272     shared->nextpxl = 0;
273     for (i=0 ; i<512 ; i+=2) shared->usepxl[i] = i+2;
274     s->shared = shared;
275   }
276 
277   while ((n--)>0) {
278     usepxl = p_hfind(shared->bypixel, P_IHASH(pixels[n]));
279     if (usepxl && usepxl[0]) usepxl[0]--;
280   }
281   deadpix.shared = shared;
282   deadpix.n = deadpix.m = 0;
283   p_hiter(shared->bycolor, &x_find_dead, &deadpix);
284   for (i=0 ; i<deadpix.m ; i++)
285     p_hinsert(shared->bycolor, deadpix.keys[i], (void*)0);
286   p_hiter(shared->bypixel, &x_list_dead, &deadpix); /* also unlinks */
287   for (i=0 ; i<deadpix.n ; i++)
288     p_hinsert(shared->bypixel, P_IHASH(deadpix.list[i]), (void*)0);
289   if (deadpix.n) {
290     Display *dpy = s->xdpy->dpy;
291     XFreeColors(dpy, DefaultColormap(dpy,s->scr_num),
292                 deadpix.list, deadpix.n, 0UL);
293   }
294 }
295 
296 static void
x_all_shared(p_scr * s,XColor * map)297 x_all_shared(p_scr *s, XColor *map /*[256]*/)
298 {
299   x_cshared *shared = s->shared;
300   if (shared) {
301     int i, n;
302     unsigned long *usepxl;
303     struct x_c2pix available;
304     Display *dpy = s->xdpy->dpy;
305     Colormap cmap = DefaultColormap(dpy,s->scr_num);
306     Visual *visual = DefaultVisual(dpy,s->scr_num);
307     int map_size = visual->map_entries;
308     if (map_size>256) map_size = 256;
309 
310     for (i=n=0 ; i<map_size ; i++)
311       if (!p_hfind(shared->bypixel, P_IHASH(i)))
312         map[n++].pixel = i;
313     if (!n) return;       /* we already own all shared colors */
314 
315     XQueryColors(dpy, cmap, map, n);
316     for (i=0 ; i<n ; i++)
317       if (XAllocColor(dpy, cmap, &map[i])) {
318         /* this color is sharable, record in bypixel table
319          * -includes the standard colors */
320         usepxl = shared->usepxl + shared->nextpxl;
321         shared->nextpxl = usepxl[0];
322         usepxl[0] = 0;     /* we haven't actually used it yet */
323         usepxl[1] = map[i].pixel;
324         p_hinsert(shared->bypixel, P_IHASH(map[i].pixel), usepxl);
325         if (shared->nextpxl>=512) break;
326       }
327     available.map = map;
328     available.n = 0;
329     p_hiter(shared->bypixel, &x_cavailable, &available);
330     XQueryColors(dpy, cmap, map, available.n);
331     for (i=0 ; i<available.n ; i++) map[i].flags = 0;
332     if (i<256) map[i].flags = 1;
333   }
334 }
335 
336 static p_col_t
x_best_shared(p_scr * s,p_col_t color,XColor * map,int n)337 x_best_shared(p_scr *s, p_col_t color, XColor *map, int n)
338 {
339   unsigned long *usepxl;
340   long d1, d0, tmp;
341   int j, k;
342   int r = P_R(color);
343   int g = P_G(color);
344   int b = P_B(color);
345   d0 = 3*256*256;
346   for (k=j=0 ; k<n ; k++) {
347     d1 =  ((tmp = map[k].red   - r), tmp*tmp);
348     d1 += ((tmp = map[k].green - g), tmp*tmp);
349     d1 += ((tmp = map[k].blue  - b), tmp*tmp);
350     if (d1<d0) j = k, d0 = d1;
351   }
352   usepxl = p_hfind(s->shared->bypixel, P_IHASH(map[j].pixel));
353   usepxl[0]++;
354   return usepxl[1];
355 }
356 
357 void
x_nuke_shared(p_scr * s)358 x_nuke_shared(p_scr *s)
359 {
360   x_cshared *shared = s->shared;
361   if (shared) {
362     unsigned long *usepxl = shared->usepxl;
363     void (*noaction)(void *)= 0;
364     Display *dpy = s->xdpy->dpy;
365     int i, n;
366     s->shared = 0;
367     p_hfree(shared->bypixel, noaction);
368     p_hfree(shared->bycolor, &x_mark_shared);
369     for (i=n=0 ; i<512 ; i+=2)
370       if (usepxl[i]==1) usepxl[n++] = usepxl[i+1];
371     if (n)
372       XFreeColors(dpy, DefaultColormap(dpy,s->scr_num), usepxl, n, 0UL);
373     p_free(usepxl);
374     p_free(shared);
375   }
376   if (p_signalling) p_abort();
377 }
378 
379 /* ARGSUSED */
380 static void
x_cavailable(void * p,p_hashkey key,void * ctx)381 x_cavailable(void *p, p_hashkey key, void *ctx)
382 {
383   unsigned long *usepxl = p;
384   struct x_c2pix *available = ctx;
385   available->map[available->n++].pixel = usepxl[1];
386 }
387 
388 /* ARGSUSED */
389 static void
x_list_dead(void * p,p_hashkey key,void * ctx)390 x_list_dead(void *p, p_hashkey key, void *ctx)
391 {
392   unsigned long *usepxl = p;
393   if (!usepxl[0]) {
394     struct x_deadpix *deadpix = ctx;
395     deadpix->list[deadpix->n++] = usepxl[1];
396     usepxl[0] = deadpix->shared->nextpxl;
397     deadpix->shared->nextpxl = usepxl - deadpix->shared->usepxl;
398   }
399 }
400 
401 static void
x_find_dead(void * p,p_hashkey key,void * ctx)402 x_find_dead(void *p, p_hashkey key, void *ctx)
403 {
404   unsigned long *usepxl = p;
405   if (!usepxl[0]) {
406     struct x_deadpix *deadpix = ctx;
407     deadpix->keys[deadpix->m++] = key;
408   }
409 }
410 
411 static void
x_mark_shared(void * p)412 x_mark_shared(void *p)
413 {
414   unsigned long *usepxl = p;
415   usepxl[0] = 1;  /* 1 cannot be in nextpxl free list (always even) */
416 }
417