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