1 
2 /*
3  *  Diverse Bristol audio routines.
4  *  Copyright (c) by Nick Copeland <nickycopeland@hotmail.com> 1996,2012
5  *
6  *
7  *   This program is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU General Public License as published by
9  *   the Free Software Foundation; either version 3 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <stdio.h>
23 #include <math.h>
24 
25 #include "brightoninternals.h"
26 #include "brightonX11.h"
27 
28 #define COLOR_START 8
29 #define SHIFT_BITS 12
30 /* This is silly, should be cleared out */
31 #define SCAN_R r
32 #define SCAN_G g
33 #define SCAN_B b
34 
35 typedef struct CCEntry {
36 	short last, next;
37 	short p_index;
38 	unsigned short g, b;
39 } cc_entry;
40 
41 typedef struct CCRow {
42 	unsigned short count;
43 	unsigned short start;
44 	cc_entry *entry;
45 } c_row;
46 
47 struct {
48 	int redshift; /* defines cache tablesize */
49 	unsigned short mask; /* defines color matching mask, f(redshift) */
50 	struct {
51 		int hits;
52 		int miss_no_row;
53 		int miss_no_color;
54 		int miss_no_entry;
55 		int miss_no_green;
56 		int miss_no_blue;
57 		int miss_eol;
58 		int inserts;
59 		int missedinserts;
60 		int deletes;
61 		int deleted;
62 		int errors;
63 		int rowinserts;
64 		int rerows;
65 		float depth;
66 		float lsd;
67 	} stats;
68 	c_row *row;
69 } c_table;
70 
71 static void
initColorCache(brightonWindow * bwin,int shift)72 initColorCache(brightonWindow *bwin, int shift)
73 {
74 	int rcount = pow(2, shift), i;
75 
76 	c_table.redshift = 16 - shift;
77 	c_table.mask = 0xffff << c_table.redshift;
78 
79 	c_table.row = brightonmalloc(sizeof(c_row) * rcount);
80 
81 	/*
82 	 * Initialise each row with COLOR_START colors. These may have to be
83 	 * rehashed later. There are arguments to make this 'none', in fact they
84 	 * are good enough to implement it now, ie, later.
85 	 */
86 	for (i = 0; i < rcount; i++)
87 		c_table.row[i].count = 0;
88 }
89 
90 extern int xcolorcount;
91 #define ASD c_table.stats.depth = c_table.stats.depth * 0.99999f + d * 0.00001f; c_table.stats.lsd = d
92 
93 void
printColorCacheStats(brightonWindow * bwin)94 printColorCacheStats(brightonWindow *bwin)
95 {
96 	int rcount = pow(2, 16 - c_table.redshift), i, j, occ, cum = 0;
97 
98 	printf("\nBrighton Color Cache Stats:\n---------------------------\n\n");
99 	printf("quality:    %4i\n", 16 - c_table.redshift);
100 	printf("redshift:   %4i\n", c_table.redshift);
101 	printf("colormask:  %4x\n", c_table.mask);
102 	printf("bucketsize: %4i\n", COLOR_START);
103 	printf("redbuckets: %4i\n", rcount);
104 	printf("\n");
105 	printf("    hits:        %8i\n", c_table.stats.hits);
106 	printf("\n");
107 
108 	printf("    miss row:    %8i    ", c_table.stats.miss_no_row);
109 	printf("    missed:      %8i\n", c_table.stats.missedinserts);
110 	printf("    miss line:   %8i    ", c_table.stats.miss_no_entry);
111 	printf("    deletes:     %8i\n", c_table.stats.deletes);
112 	printf("    miss EOL:    %8i    ", c_table.stats.miss_eol);
113 	printf("    deleted:     %8i\n", c_table.stats.deleted);
114 	printf("    miss green:  %8i    ", c_table.stats.miss_no_green);
115 	printf("    errors:      %8i\n", c_table.stats.errors);
116 	printf("    miss blue:   %8i    ", c_table.stats.miss_no_blue);
117 	printf("    new rows:    %8i\n", c_table.stats.rowinserts);
118 	printf("    miss color:  %8i    ", c_table.stats.miss_no_color);
119 	printf("    new buckets: %8i\n", c_table.stats.rerows);
120 	printf("    misses total:%8i    ",
121 		c_table.stats.miss_eol +
122 		c_table.stats.miss_no_row +
123 		c_table.stats.miss_no_color +
124 		c_table.stats.miss_no_entry +
125 		c_table.stats.miss_no_green +
126 		c_table.stats.miss_no_blue);
127 	printf("    inserts:     %8i\n", c_table.stats.inserts);
128 	printf("\n");
129 	printf("    ASD:         %8.1f    ", c_table.stats.depth);
130 	printf("    LSD:         %8.1f\n", c_table.stats.lsd);
131 	printf("\n");
132 	printf("Red bucket stats:\n");
133 	printf("----------------------------------");
134 	printf("----------------------------------\n");
135 	for (i = 0; i < (rcount>>1); i++)
136 	{
137 		occ = 0;
138 		if (c_table.row[i].count > 0)
139 		{
140 			for (j = c_table.row[i].start;
141 					j >= 0;
142 					j = c_table.row[i].entry[j].next)
143 				occ++;
144 		}
145 		printf("%3i: sz %5i, st %3i, occ %5i | ", i,
146 			c_table.row[i].count,
147 			c_table.row[i].start,
148 			occ);
149 		cum += occ;
150 
151 		occ = 0;
152 		if (c_table.row[i+(rcount>>1)].count > 0)
153 		{
154 			for (j = c_table.row[i+(rcount>>1)].start;
155 					j >= 0;
156 					j = c_table.row[i+(rcount>>1)].entry[j].next)
157 				occ++;
158 		}
159 		printf("%3i: sz %5i, st %3i, occ %5i\n", i+(rcount>>1),
160 			c_table.row[i+(rcount>>1)].count,
161 			c_table.row[i+(rcount>>1)].start,
162 			occ);
163 		cum += occ;
164 	}
165 	printf("----------------------------------");
166 	printf("----------------------------------\n");
167 
168 	/*
169 	 * Scan the table of colors to see how much of the palette has not been
170 	 * allocated by the library.
171 	 */
172 	occ = 0;
173 	for (i = 0; i < bwin->cmap_size; i++)
174 		if ((bwin->display->palette[i].uses > 0) &&
175 			((bwin->display->palette[i].gc == 0) &&
176 			 (bwin->display->palette[i].pixel < 0)))
177 			occ++;
178 
179 	/*
180 	 * Color stats: the numbers typically appear to not add up, the cache has
181 	 * most, more than in the dumped XPM files and in the middle sits the
182 	 * number allocated by the window system interface library (B11). The
183 	 * reason is that the cache contains all the colors requested from all the
184 	 * bitmap files - not all colors may be rendered since the bitmaps are
185 	 * scaled/rotated, etc, and some pixels are missed out of the drawn image
186 	 * however are still in the cache if they are needed.
187 	 *
188 	 * The interface library has more than in the dumped image since the image
189 	 * changes due to shading and transparencies, so colors that may have been
190 	 * rendered in one image may not be in the next depending on location of
191 	 * the controllers and panels once drawn then withdrawn often have a
192 	 * lot of unseen controls. It will probably never have the same value as
193 	 * the cache since GC are scarce and are only requested in the interface
194 	 * library when they are actually required to be painted.
195 	 *
196 	 * So, the following figures should more or less add up. There is always a
197 	 * difference of '1' due to the use of 'Blue' as transparency, hence never
198 	 * rendered.
199 	 */
200 	printf("Total cache entries: %i, Window System %i, no GC (unused): %i\n",
201 		cum, xcolorcount, occ);
202 
203 	printf("\n");
204 }
205 
206 /*
207  * When a row fills up then we have to rebuild it in a new longer cache line.
208  */
209 static int
cacheReRow(unsigned short row)210 cacheReRow(unsigned short row)
211 {
212 	cc_entry *n_entry;
213 	int i, new = 0;
214 
215 	n_entry = brightonmalloc(sizeof(cc_entry)
216 		* (c_table.row[row].count += COLOR_START));
217 
218 	/*
219 	 * Initialise the new table
220 	 */
221 	for (i = 0; i < c_table.row[row].count; i++)
222 		n_entry[i].last = n_entry[i].next = n_entry[i].p_index = -1;
223 
224 	/*
225 	 * Insert the first entry
226 	 */
227 	i = c_table.row[row].start;
228 	n_entry[new].g = c_table.row[row].entry[i].g;
229 	n_entry[new].b = c_table.row[row].entry[i].b;
230 	n_entry[new].p_index = c_table.row[row].entry[i].p_index;
231 
232 	/*
233 	 * Move to the next entry and start the convergence.
234 	 */
235 	i = c_table.row[row].entry[i].next;
236 
237 	for (; i >= 0; i = c_table.row[row].entry[i].next)
238 	{
239 		new++;
240 		/*
241 		 * Take this entry and converge it to the new list
242 		 */
243 		n_entry[new].g = c_table.row[row].entry[i].g;
244 		n_entry[new].b = c_table.row[row].entry[i].b;
245 		n_entry[new].p_index = c_table.row[row].entry[i].p_index;
246 		n_entry[new].last = new - 1;
247 		n_entry[new].next = -1;
248 		n_entry[new - 1].next = new;
249 	}
250 
251 	brightonfree(c_table.row[row].entry);
252 
253 	c_table.row[row].entry = n_entry;
254 	c_table.row[row].start = 0;
255 
256 	c_table.stats.rerows++;
257 
258 	return(0);
259 }
260 
261 /*
262  * There are a couple of things we have to watch out for. Firstly we have to
263  * correctly reorder each red row when a color is inserted. Then if we have
264  * reached the end of the table we need to insert a larger size for this red
265  * hue and convert the existing one over. Hm.
266  */
267 int
cacheInsertColor(unsigned short r,unsigned short g,unsigned short b,unsigned short p_index)268 cacheInsertColor(unsigned short r, unsigned short g, unsigned short b,
269 unsigned short p_index)
270 {
271 	c_row *row;
272 	cc_entry *entry;
273 	int i, j, free;
274 
275 	/*
276 	 * Get our red row. If it is new stuff in the values.
277 	 */
278 	row = &c_table.row[SCAN_R >> c_table.redshift];
279 
280 	/*
281 	 * So we have a free entry. Now we need to scan to find the target point
282 	 * to insert the new color
283 	 *
284 	 * Make these values 'searchable':
285 	 */
286 	SCAN_G &= c_table.mask;
287 	SCAN_B &= c_table.mask;
288 
289 	if ((entry = row->entry) <= 0)
290 	{
291 //printf("insert NEW: 0 (%i)\n", r>>c_table.redshift);
292 		/*
293 		 * Create the row if it has not been done, by implication this already
294 		 * means we should generate a new color, done in the calling party.
295 		 */
296 		row->count = COLOR_START;
297 		row->start = 0;
298 
299 		c_table.row[SCAN_R >> c_table.redshift].entry =
300 			brightonmalloc(sizeof(cc_entry) * COLOR_START);
301 
302 		for (i = 0; i < COLOR_START; i++)
303 			row->entry[i].last = row->entry[i].next = row->entry[i].p_index
304 				= -1;
305 
306 		row = &c_table.row[SCAN_R >> c_table.redshift];
307 
308 		row->entry[0].last = row->entry[0].next = -1;
309 		row->entry[0].p_index = p_index;
310 		row->entry[0].g = SCAN_G;
311 		row->entry[0].b = SCAN_B;
312 
313 		c_table.stats.rowinserts++;
314 		c_table.stats.inserts++;
315 
316 		return(0);
317 	}
318 
319 	/*
320 	 * We need a free entry in this row to proceed
321 	 */
322 	for (free = 0; free < row->count; free++)
323 		if (row->entry[free].p_index < 0)
324 			break;
325 
326 	/*
327 	 * If we passed the end of row then it is full. We need to rebuild the row
328 	 * but will work that later as it comes up. For now, return blue.
329 	 */
330 	if (free == row->count)
331 	{
332 		c_table.stats.missedinserts++;
333 
334 		cacheReRow(r>>c_table.redshift);
335 
336 		entry = row->entry;
337 	}
338 
339 	entry[free].last = -1;
340 	entry[free].next = -1;
341 	entry[free].g = SCAN_G;
342 	entry[free].b = SCAN_B;
343 	entry[free].p_index = p_index;
344 
345 	/*
346 	 * We first want to scan for green, that will be more efficient than
347 	 * scanning that both green and blue match as this can terminate earlier.
348 	 *
349 	 * We have to start with the first entry since it may need extra processing.
350 	 */
351 	if ((entry[row->start].g > g) ||
352 		((entry[row->start].g == g) && (entry[row->start].b > b)))
353 	{
354 		/*
355 		 * If start is already greater then insert our new entry.
356 		 */
357 		entry[free].next = row->start;
358 		entry[free].last = -1;
359 		entry[row->start].last = free;
360 		row->start = free;
361 
362 //printf("insert SOL: %i (%i)\n", free, r>>c_table.redshift);
363 		c_table.stats.inserts++;
364 		return(free);
365 	}
366 
367 	j = row->start;
368 	while (entry[j].SCAN_G < SCAN_G)
369 	{
370 		/*
371 		 * If we have hit the end of the line and our new green is still
372 		 * bigger append the new entry and return.
373 		 */
374 		if (entry[j].next < 0)
375 		{
376 			entry[j].next = free;
377 			entry[free].last = j;
378 //printf("insert EOS: %i (%i)\n", free, r>>c_table.redshift);
379 
380 			c_table.stats.inserts++;
381 			return(free);
382 		}
383 
384 		j = entry[j].next;
385 	}
386 
387 	/*
388 	 * At this point we have scanned to where the cached green is equal
389 	 * to the targetted value. Now scan for a matching blue until end of
390 	 * list or green no longer matches.
391 	 */
392 	for (; j >= 0; j = entry[j].next)
393 	{
394 		/*
395 		 * Check green still matches. If it doesn't then insert the new entry
396 		 * behind this one?
397 		 */
398 		if ((entry[j].SCAN_G != SCAN_G) ||
399 			(entry[j].SCAN_B > SCAN_B))
400 		{
401 			entry[free].next = j;
402 			entry[free].last = entry[j].last;
403 			entry[entry[j].last].next = free;
404 			entry[j].last = free;
405 
406 //printf("insert INL: %i (%i) %i: %x %x/%x %x\n", free, r>>c_table.redshift,
407 //SCAN_R>>c_table.redshift, SCAN_G, entry[j].SCAN_G, SCAN_B, entry[j].SCAN_B);
408 
409 			c_table.stats.inserts++;
410 			return(free);
411 		}
412 
413 		/*
414 		 * If the next entry is the end of the list then append this new one
415 		 */
416 		if (entry[j].next < 0)
417 		{
418 			entry[free].last = j;
419 			entry[j].next = free;
420 //printf("insert EOL: %i (%i)\n", free, r>>c_table.redshift);
421 
422 			c_table.stats.inserts++;
423 			return(free);
424 		}
425 	}
426 
427 	/*
428 	 * So we are at the end of the list? Hm. Return "blue".
429 	printf("colour cache insert: we should not have got here\n");
430 	 */
431 	c_table.stats.errors++;
432 	return(0);
433 }
434 
435 static void
cacheFreeColor(unsigned short r,unsigned short g,unsigned short b,int pindex)436 cacheFreeColor(unsigned short r, unsigned short g, unsigned short b, int pindex)
437 {
438 	c_table.stats.deletes++;
439 
440 	/*
441 	 * Search through the row depicted by R for the selected pindex.
442 	 *
443 	 * Hm, leave this for now - we are not actually big on deleting GC due to
444 	 * the way the cache works. If deletes gets out of sync with deleted then
445 	 * we can implement this.
446 	 *
447 	for (i = c_table.row[r >> c_table.redshift]; i >= 0; j = entry[i].next)
448 	{
449 		if (
450 	}
451 	 */
452 }
453 
454 static int
cacheFindColor(unsigned short r,unsigned short g,unsigned short b,int cm)455 cacheFindColor(unsigned short r, unsigned short g, unsigned short b, int cm)
456 {
457 	c_row *row;
458 	cc_entry *entry;
459 	int j = 0;
460 	int d = 0;
461 	unsigned short mask = c_table.mask; // 0xffff << (16 - cm);
462 
463 	if ((r == 0) && (g == 0) && (b == 0xff00))
464 		return(0);
465 
466 	/*
467 	 * First select the row using a hash of the red value
468 	 */
469 	if ((row = &c_table.row[SCAN_R >> c_table.redshift]) <= 0)
470 	{
471 		c_table.stats.miss_no_row++;
472 		return(-1);
473 	}
474 
475 	/*
476 	 * Make these values 'searchable'
477 	 */
478 	SCAN_G &= mask;
479 	SCAN_B &= mask;
480 
481 	/*
482 	 * We have the red matched, scan through the current line for a green
483 	 * match.
484 	 */
485 	if ((entry = row->entry) <= 0)
486 	{
487 		c_table.stats.miss_no_entry++;
488 		return(-1);
489 	}
490 
491 	/*
492 	 * We first want to scan for green, that will be more efficient than
493 	 * scanning that both green and blue match as this can terminate earlier.
494 	 */
495 	for (j = row->start; j >= 0; j = entry[j].next)
496 	{
497 		if ((d++) > 10000)
498 			return(-1);
499 
500 		if (entry[j].SCAN_G == SCAN_G)
501 			break;
502 
503 		if (entry[j].SCAN_G > SCAN_G)
504 		{
505 			c_table.stats.miss_no_green++;
506 			ASD;
507 			return(-1);
508 		}
509 
510 		/*
511 		 * If we hit the end of the scan and still have no match on green
512 		 * then return.
513 		 */
514 		if (entry[j].next < 0)
515 		{
516 			c_table.stats.miss_eol++;
517 			ASD;
518 			return(-1);
519 		}
520 	}
521 
522 	/*
523 	 * At this point we have scanned to where the cached green is greater/equal
524 	 * to the targetted value. Now scan for a matching blue until end of
525 	 * list or green no longer matches.
526 	 */
527 	for (; j >= 0; j = entry[j].next)
528 	{
529 		if ((d++) > 10000)
530 			return(-1);
531 
532 		/* Check green still matches */
533 		if (entry[j].SCAN_G != SCAN_G)
534 		{
535 			c_table.stats.miss_no_color++;
536 			ASD;
537 			return(-1);
538 		}
539 		if (entry[j].SCAN_B > SCAN_B)
540 		{
541 			c_table.stats.miss_no_blue++;
542 			ASD;
543 			return(-1);
544 		}
545 
546 		/*
547 		 * If this is a blue match, return the palette
548 		 */
549 		if (entry[j].SCAN_B == SCAN_B)
550 		{
551 			c_table.stats.hits++;
552 
553 			ASD;
554 //printf("found %i\n", entry[j].p_index);
555 			return(entry[j].p_index);
556 		}
557 	}
558 
559 	c_table.stats.miss_no_color++;
560 	ASD;
561 
562 	/*
563 	 * If we get here then we have scanned until end of list. We could insert
564 	 * the new color however we will leave that to the calling party as we
565 	 * don't have a palette index.
566 	 */
567 	return(-1);
568 }
569 
570 /*
571  * See if we already have this color somewhere. This can be a slow operation
572  * so we will make a couple of changes. Firstly, we will use a color cache to
573  * accelerate the searches, and in the event of not finding the color then it
574  * should be built in here.
575  *
576  * Cache will consist of a table hashed by red, with each table entry being
577  * sorted then by green. As such we can jump very fast to the red matches and
578  * search the greens for a blue match.
579  *
580  * We should add another call to insert a hashed entry.
581  */
582 int
brightonFindColor(brightonPalette * palette,int ncolors,unsigned short r,unsigned short g,unsigned short b,int match)583 brightonFindColor(brightonPalette *palette, int ncolors,
584 unsigned short r, unsigned short g, unsigned short b, int match)
585 {
586 	register int i;
587 	register unsigned short rmin, rmax, gmin, gmax, bmin, bmax;
588 	float lesser = match , greater = 1 / match;
589 
590 	return(cacheFindColor(r, g, b, match));
591 
592 	/* printf("find %i, %i, %i %f %i\n", r, g, b, match, ncolors); */
593 
594 	rmin = (lesser * ((float) r));
595 	if ((greater * ((float) r)) > 65535)
596 		rmax = 65535;
597 	else
598 		rmax = (greater * ((float) r));
599 
600 	gmin = (lesser * ((float) g));
601 	if ((greater * ((float) g)) > 65535)
602 		gmax = 65535;
603 	else
604 		gmax = (greater * ((float) g));
605 
606 	bmin = (lesser * ((float) b));
607 	if ((greater * ((float) b)) > 65535)
608 		bmax = 65535;
609 	else
610 		bmax = (greater * ((float) b));
611 
612 	if (lesser > greater)
613 		lesser = greater = 1.0;
614 
615 	for (i = 0; i < ncolors; i++)
616 	{
617 		if (palette[i].flags & BRIGHTON_INACTIVE_COLOR)
618 			continue;
619 
620 		if ((palette[i].red >= rmin) &&
621 			(palette[i].red <= rmax) &&
622 			(palette[i].green >= gmin) &&
623 			(palette[i].green <= gmax) &&
624 			(palette[i].blue >= bmin) &&
625 			(palette[i].blue <= bmax))
626 			return(i);
627 	}
628 	return(-1);
629 }
630 
631 int
brightonFindFreeColor(brightonPalette * palette,int ncolors)632 brightonFindFreeColor(brightonPalette *palette, int ncolors)
633 {
634 	int i;
635 
636 	for (i = 0; i < ncolors; i++)
637 		if (palette[i].flags & BRIGHTON_INACTIVE_COLOR)
638 			return(i);
639 
640 	return(-1);
641 }
642 
643 int
brightonFreeGC(brightonWindow * bwin,int index)644 brightonFreeGC(brightonWindow *bwin, int index)
645 {
646 	if (index < 0)
647 		return(0);
648 	if (index >= bwin->cmap_size)
649 		return(0);
650 	if (--bwin->display->palette[index].uses == 0)
651 	{
652 		BFreeColor(bwin->display, &bwin->display->palette[index]);
653 
654 		cacheFreeColor(
655 			bwin->display->palette[index].red,
656 			bwin->display->palette[index].green,
657 			bwin->display->palette[index].blue,
658 			index);
659 	}
660 	return(0);
661 }
662 
663 /*
664  * The primary use of GCs is for color selection, and then also primarily in the
665  * forgreound. We are going to request colors as R/G/B tuples, as yet not with
666  * any structure management although this may happen. We will consider the use
667  * of hash table lookup with best fit, limiting the total number of colours
668  * that brighton can reserve, and keep stats on lookup performance for different
669  * hashing functions.
670  *
671  * To minimise color requirements it is preferable for multiple synths to be
672  * children, ie, share the same contexts.
673  */
674 int
brightonGetGC(brightonWindow * bwin,unsigned short r,unsigned short g,unsigned short b)675 brightonGetGC(brightonWindow *bwin,
676 unsigned short r, unsigned short g, unsigned short b)
677 {
678 	register int pindex;
679 
680 	/*printf("brightonGetGC(%x, %x, %x)\n", r, g, b); */
681 
682 	/*
683 	 * See if we can find this color
684 	 */
685 	if ((pindex = cacheFindColor(r, g, b, bwin->quality)) >= 0)
686 	{
687 		bwin->display->palette[pindex].uses++;
688 		return(pindex);
689 	}
690 	/*
691 	if ((pindex = brightonFindColor(bwin->display->palette, bwin->cmap_size,
692 		r, g, b, bwin->quality)) >= 0)
693 	{
694 		bwin->display->palette[pindex].uses++;
695 		return(pindex);
696 	}
697 	*/
698 
699 	/*
700 	 * If we have no free colors, then palette[0] is the default
701 	 */
702 	if ((pindex =
703 		brightonFindFreeColor(bwin->display->palette, bwin->cmap_size)) < 0)
704 		return(0);
705 
706 	bwin->display->palette[pindex].red =  r;
707 	bwin->display->palette[pindex].green = g;
708 	bwin->display->palette[pindex].blue = b;
709 	bwin->display->palette[pindex].uses++;
710 
711 	bwin->display->palette[pindex].flags &= ~BRIGHTON_INACTIVE_COLOR;
712 	bwin->display->palette[pindex].uses++;
713 
714 	cacheInsertColor(r, g, b, pindex);
715 
716 	return(pindex);
717 }
718 
719 /*
720  * With named colors we need to allocate them in advance, we cannot leave this
721  * up to the BRendering process. We should only really be using this for one
722  * color as the match is exact, however the XPM bitmap format accepts names
723  * as well as RGB even though pretty much all of the shipped bitmaps avoid
724  * using names ("Blue" is just re-interpreted from 'none' in the bitmap) and
725  * has to be exact to ensure transparency.
726  */
727 int haveblue = -1;
728 
729 int
brightonGetGCByName(brightonWindow * bwin,char * name)730 brightonGetGCByName(brightonWindow *bwin, char *name)
731 {
732 	int pindex;
733 
734 	if ((strcmp(name, "Blue") == 0) && (haveblue >= 0))
735 	{
736 		/*
737 		 * We only want to map blue once even though it may well be requested
738 		 * from multiple bitmaps.
739 		 */
740 		bwin->display->palette[haveblue].uses++;
741 		return(haveblue);
742 	}
743 
744 	/*
745 	 * If we have no free colors, then palette[0] is the default. This is
746 	 * typically "Blue" which may have been a bad choice.....
747 	 */
748 	if ((pindex =
749 		brightonFindFreeColor(bwin->display->palette, bwin->cmap_size)) < 0)
750 		return(0);
751 
752 	bwin->display->palette[pindex].uses++;
753 
754 	BAllocColorByName(bwin->display, &bwin->display->palette[pindex], name);
755 
756 	bwin->display->palette[pindex].flags &= ~BRIGHTON_INACTIVE_COLOR;
757 	bwin->display->palette[pindex].uses++;
758 
759 	if (strcmp(name, "Blue") == 0)
760 		haveblue = pindex;
761 
762 	return(pindex);
763 }
764 
765 /*
766  * Make sure we have a suitable visual, then try and get a shitload of colors.
767  * We should consider looking for any existing visuals of an acceptable type?
768  */
769 brightonPalette *
brightonInitColormap(brightonWindow * bwin,int ncolors)770 brightonInitColormap(brightonWindow *bwin, int ncolors)
771 {
772 	/*printf("brightonInitColormap(%i)\n", ncolors); */
773 
774 	initColorCache(bwin, bwin->quality); /* Init cache with digit shift */
775 
776 	if (bwin->display->palette == NULL)
777 	{
778 		int i;
779 
780 		bwin->display->palette = (brightonPalette *)
781 			brightonmalloc(ncolors * sizeof(brightonPalette));
782 
783 		for (i = 0; i < ncolors; i++)
784 		{
785 			bwin->display->palette[i].flags |= BRIGHTON_INACTIVE_COLOR;
786 			bwin->display->palette[i].pixel = -1;
787 		}
788 	}
789 
790 	return(BInitColorMap(bwin->display));
791 }
792 
793 void
brightonSprintColor(brightonWindow * bwin,char * cstring,int pixel)794 brightonSprintColor(brightonWindow *bwin, char *cstring, int pixel)
795 {
796 	sprintf(cstring, "#%02x%02x%02x",
797 		bwin->display->palette[pixel].red >> 8,
798 		bwin->display->palette[pixel].green >> 8,
799 		bwin->display->palette[pixel].blue >> 8);
800 }
801 
802