1 /*
2  * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /* Iterative color palette generation */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #ifdef TIMES
32 #include <time.h>
33 #endif /* TIMES */
34 
35 #ifndef MAKECUBE_EXE
36 #include "jvm.h"
37 #include "jni_util.h"
38 
39 extern JavaVM *jvm;
40 #endif
41 #include "img_colors.h"
42 
43 #define jio_fprintf fprintf
44 
45 #define TRUE 1
46 #define FALSE 0
47 static float monitor_gamma[3] = {2.6f, 2.6f, 2.4f}; /* r,g,b */
48 static float mat[3][3] = {
49     {0.3811f, 0.2073f, 0.0213f},
50     {0.3203f, 0.6805f, 0.1430f},
51     {0.2483f, 0.1122f, 1.2417f}
52 };
53 static float whiteXYZ[3] = { 0.9497f, 1.0000f, 1.4060f };
54 #define whitex (0.9497f / (0.9497f + 1.0000f + 1.4060f))
55 #define whitey (1.0000f / (0.9497f + 1.0000f + 1.4060f))
56 static float uwht = 4*whitex/(-2*whitex + 12*whitey + 3);
57 static float vwht = 9*whitey/(-2*whitex + 12*whitey + 3);
58 
59 static float Rmat[3][256];
60 static float Gmat[3][256];
61 static float Bmat[3][256];
62 static float Ltab[256], Utab[256], Vtab[256];
63 
64 typedef struct {
65     unsigned char red;
66     unsigned char green;
67     unsigned char blue;
68     unsigned char bestidx;
69     int nextidx;
70     float L, U, V;
71     float dist;
72     float dE;
73     float dL;
74 } CmapEntry;
75 
76 static int num_virt_cmap_entries;
77 static CmapEntry *virt_cmap;
78 static int prevtest[256];
79 static int nexttest[256];
80 
81 static float Lscale = 10.0f;
82 /* this is a multiplier--it should not be zero */
83 static float Weight = 250.0f;
84 
85 #define WEIGHT_DIST(d,l)   (Weight*(d)/(Weight+(l)))
86 #define UNWEIGHT_DIST(d,l) ((Weight+(l))*(d)/Weight)
87 
88 #if 0
89 #define WEIGHT_DIST(d,l) (d)
90 #define UNWEIGHT_DIST(d,l) (d)
91 #endif
92 
93 static void
init_matrices()94 init_matrices()
95 {
96     static int done = 0;
97     int i;
98 
99     if (done) {
100         return;
101     }
102     for (i = 0; i < 256; ++i)
103     {
104         float iG = (float) pow(i/255.0, monitor_gamma[0]);
105         Rmat[0][i] = mat[0][0] * iG;
106         Rmat[1][i] = mat[0][1] * iG;
107         Rmat[2][i] = mat[0][2] * iG;
108 
109         iG = (float) pow(i/255.0, monitor_gamma[1]);
110         Gmat[0][i] = mat[1][0] * iG;
111         Gmat[1][i] = mat[1][1] * iG;
112         Gmat[2][i] = mat[1][2] * iG;
113 
114         iG = (float) pow(i/255.0, monitor_gamma[2]);
115         Bmat[0][i] = mat[2][0] * iG;
116         Bmat[1][i] = mat[2][1] * iG;
117         Bmat[2][i] = mat[2][2] * iG;
118     }
119     done = 1;
120 }
121 
122 static void
LUV_convert(int red,int grn,int blu,float * L,float * u,float * v)123 LUV_convert(int red, int grn, int blu, float *L, float *u, float *v)
124 {
125     float X = Rmat[0][red] + Gmat[0][grn] + Bmat[0][blu];
126     float Y = Rmat[1][red] + Gmat[1][grn] + Bmat[1][blu];
127     float Z = Rmat[2][red] + Gmat[2][grn] + Bmat[2][blu];
128     float sum = X+Y+Z;
129 
130     if (sum != 0.0f) {
131         float x    = X/sum;
132         float y    = Y/sum;
133         float dnm  = -2*x + 12*y + 3;
134         float ytmp = (float) pow(Y/whiteXYZ[1], 1.0/3.0);
135 
136         if (ytmp < .206893f) {
137             *L = 903.3f*Y/whiteXYZ[1];
138         } else {
139             *L = 116*(ytmp) - 16;
140         }
141         if (dnm != 0.0f) {
142             float uprm = 4*x/dnm;
143             float vprm = 9*y/dnm;
144 
145             *u = 13*(*L)*(uprm-uwht);
146             *v = 13*(*L)*(vprm-vwht);
147         } else {
148             *u = 0.0f;
149             *v = 0.0f;
150         }
151     } else {
152         *L = 0.0f;
153         *u = 0.0f;
154         *v = 0.0f;
155     }
156 }
157 
158 static int cmapmax;
159 static int total;
160 static unsigned char cmap_r[256], cmap_g[256], cmap_b[256];
161 
162 #define DIST_THRESHOLD 7
163 static int
no_close_color(float l,float u,float v,int c_tot,int exact)164 no_close_color(float l, float u, float v, int c_tot, int exact) {
165     int i;
166     for (i = 0; i < c_tot; ++i) {
167         float t, dist = 0.0f;
168         t = Ltab[i] - l; dist += t*t*Lscale;
169         t = Utab[i] - u; dist += t*t;
170         t = Vtab[i] - v; dist += t*t;
171 
172         if (dist < (exact ? 0.1 : DIST_THRESHOLD))
173             return 0;
174     }
175 
176     return 1;
177 }
178 
179 static int
add_color(int r,int g,int b,int f)180 add_color(int r, int g, int b, int f) {
181     if (total >= cmapmax)
182         return 0;
183     cmap_r[total] = r;
184     cmap_g[total] = g;
185     cmap_b[total] = b;
186     LUV_convert(cmap_r[total],cmap_g[total],cmap_b[total],
187                 Ltab + total, Utab + total, Vtab + total);
188     if (no_close_color(Ltab[total], Utab[total], Vtab[total], total-1, f)) {
189         ++total;
190         return 1;
191     } else {
192         return 0;
193     }
194 }
195 
196 static void
init_primaries()197 init_primaries() {
198     int r, g, b;
199 
200     for (r = 0; r < 256; r += (r?128:127)) {
201         for (g = 0; g < 256; g += (g?128:127)) {
202             for (b = 0; b < 256; b += (b?128:127)) {
203                 if ((r == g) && (g == b)) continue; /* black or white */
204                 add_color(r, g, b, TRUE);
205             }
206         }
207     }
208 }
209 
210 static void
init_pastels()211 init_pastels() {
212     int i;
213     /* very light colors */
214     for (i = 6; i >= 0; --i)
215         add_color((i&4) ? 0xff : 0xf0,
216                   (i&2) ? 0xff : 0xf0,
217                   (i&1) ? 0xff : 0xf0, TRUE);
218 }
219 
220 static void
init_grays()221 init_grays() {
222     int i;
223     for (i = 15; i < 255; i += 16)
224         add_color(i, i, i, TRUE);
225 }
226 
227 static void
init_mac_palette()228 init_mac_palette() {
229     add_color(255, 255, 204, TRUE);
230     add_color(255, 255, 0,   TRUE);
231     add_color(255, 204, 153, TRUE);
232     add_color(255, 102, 204, TRUE);
233     add_color(255, 102, 51,  TRUE);
234     add_color(221, 0, 0,     TRUE);
235     add_color(204, 204, 255, TRUE);
236     add_color(204, 153, 102, TRUE);
237     add_color(153, 255, 255, TRUE);
238     add_color(153, 153, 255, TRUE);
239     add_color(153, 102, 153, TRUE);
240     add_color(153, 0, 102,   TRUE);
241     add_color(102, 102, 204, TRUE);
242     add_color(51, 255, 153,  TRUE);
243     add_color(51, 153, 102,  TRUE);
244     add_color(51, 102, 102,  TRUE);
245     add_color(51, 51, 102,   TRUE);
246     add_color(51, 0, 153,    TRUE);
247     add_color(0, 187, 0,     TRUE);
248     add_color(0, 153, 255,   TRUE);
249     add_color(0, 0, 221,     TRUE);
250 }
251 
252 static void
init_virt_cmap(int tablesize,int testsize)253 init_virt_cmap(int tablesize, int testsize)
254 {
255     int r, g, b;
256     int gray = -1;
257     CmapEntry *pCmap;
258     unsigned int dotest[256];
259 
260     if (virt_cmap) {
261         free(virt_cmap);
262         virt_cmap = 0;
263     }
264 
265     num_virt_cmap_entries = tablesize * tablesize * tablesize;
266     virt_cmap = malloc(sizeof(CmapEntry) * num_virt_cmap_entries);
267     /*
268      * Fix for bug 4070647 malloc return value not check in img_colors.c
269      * We have to handle the malloc failure differently under
270      * Win32 and Solaris since under Solaris this file is linked with
271      * libawt.so and under Win32 it's a separate awt_makecube.exe
272      * application.
273      */
274     if (virt_cmap == NULL) {
275 #ifndef MAKECUBE_EXE
276         JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
277         JNU_ThrowOutOfMemoryError(env, "init_virt_cmap: OutOfMemoryError");
278         return;
279 #else
280         fprintf(stderr,"init_virt_cmap: OutOfMemoryError\n");
281         exit(-1);
282 #endif
283     }
284     pCmap = virt_cmap;
285     for (r = 0; r < total; r++) {
286         if (cmap_r[r] == cmap_g[r] && cmap_g[r] == cmap_b[r]) {
287             if (gray < 0 || cmap_r[gray] < cmap_r[r]) {
288                 gray = r;
289             }
290         }
291     }
292     if (gray < 0) {
293 #ifdef DEBUG
294         jio_fprintf(stderr, "Didn't find any grays in color table!\n");
295 #endif /* DEBUG */
296         gray = 0;
297     }
298     g = 0;
299     b = 0;
300     for (r = 0; r < tablesize - 1; ++r) {
301         if (g >= 0) {
302             b = r;
303             dotest[r] = 1;
304             g -= tablesize;
305         } else {
306             dotest[r] = 0;
307         }
308         prevtest[r] = b;
309         g += testsize;
310     }
311     b = r;
312     prevtest[r] = b;
313     dotest[r] = 1;
314     for (r = tablesize - 1; r >= 0; --r) {
315         if (prevtest[r] == r) {
316             b = r;
317         }
318         nexttest[r] = b;
319     }
320 #ifdef DEBUG
321     for (r = 0; r < tablesize; ++r) {
322         if (dotest[r]) {
323             if (prevtest[r] != r || nexttest[r] != r) {
324                 jio_fprintf(stderr, "prev/next != r!\n");
325             }
326         }
327     }
328 #endif /* DEBUG */
329     for (r = 0; r < tablesize; ++r)
330     {
331         int red = (int)(floor(r*255.0/(tablesize - 1)));
332         for (g = 0; g < tablesize; ++g)
333         {
334             int green = (int)(floor(g*255.0/(tablesize - 1)));
335             for (b = 0; b < tablesize; ++b)
336             {
337                 int blue = (int)(floor(b*255.0/(tablesize - 1)));
338                 float t, d;
339                 if (pCmap >= virt_cmap + num_virt_cmap_entries) {
340 #ifdef DEBUG
341                     jio_fprintf(stderr, "OUT OF pCmap CONVERSION SPACE!\n");
342 #endif /* DEBUG */
343                     continue;           /* Shouldn't happen */
344                 }
345                 pCmap->red = red;
346                 pCmap->green = green;
347                 pCmap->blue = blue;
348                 LUV_convert(red, green, blue, &pCmap->L, &pCmap->U, &pCmap->V);
349                 if ((red != green || green != blue) &&
350                     (!dotest[r] || !dotest[g] || !dotest[b]))
351                 {
352                     pCmap->nextidx = -1;
353                     pCmap++;
354                     continue;
355                 }
356                 pCmap->bestidx = gray;
357                 pCmap->nextidx = 0;
358                 t = Ltab[gray] - pCmap->L;
359                 d = t * t;
360                 if (red == green && green == blue) {
361                     pCmap->dist = d;
362                     d *= Lscale;
363                 } else {
364                     d *= Lscale;
365                     t = Utab[gray] - pCmap->U;
366                     d += t * t;
367                     t = Vtab[gray] - pCmap->V;
368                     d += t * t;
369                     pCmap->dist = d;
370                 }
371                 pCmap->dE = WEIGHT_DIST(d, pCmap->L);
372                 pCmap++;
373             }
374         }
375     }
376 #ifdef DEBUG
377     if (pCmap < virt_cmap + num_virt_cmap_entries) {
378         jio_fprintf(stderr, "Didn't fill pCmap conversion table!\n");
379     }
380 #endif /* DEBUG */
381 }
382 
383 static int
find_nearest(CmapEntry * pCmap)384 find_nearest(CmapEntry *pCmap) {
385     int red = pCmap->red;
386     int grn = pCmap->green;
387     int blu = pCmap->blue;
388     float L = pCmap->L;
389     float dist;
390     int i;
391 
392     if ((red == grn) && (grn == blu)) {
393         dist = pCmap->dist;
394 
395         for (i = pCmap->nextidx; i < total; ++i) {
396             float dL;
397 
398             if (cmap_r[i] != cmap_g[i] || cmap_g[i] != cmap_b[i]) {
399                 continue;
400             }
401 
402             dL = Ltab[i] - L; dL *= dL;
403 
404             if (dL < dist) {
405                 dist = dL;
406                 pCmap->dist = dist;
407                 pCmap->dL = dist;
408                 pCmap->dE = WEIGHT_DIST(dist*Lscale,L);
409                 pCmap->bestidx = i;
410             }
411         }
412         pCmap->nextidx = total;
413     } else {
414         float U = pCmap->U;
415         float V = pCmap->V;
416         dist = pCmap->dist;
417 
418         for (i = pCmap->nextidx; i < total; ++i) {
419             float dL, dU, dV, dE;
420             dL = Ltab[i] - L; dL *= (dL*Lscale);
421             dU = Utab[i] - U; dU *= dU;
422             dV = Vtab[i] - V; dV *= dV;
423 
424             dE = dL + dU + dV;
425             if (dE < dist)
426             {
427                 dist = dE;
428                 /* *delta = (dL/4) + dU + dV; */
429                 /* *delta = dist */
430                 /* *delta = dL + 100*(dU+dV)/(100+L); */
431                 pCmap->dist = dist;
432                 pCmap->dE = WEIGHT_DIST(dE, L);
433                 pCmap->dL = dL/Lscale;
434                 pCmap->bestidx = i;
435             }
436         }
437         pCmap->nextidx = total;
438     }
439 
440     return pCmap->bestidx;
441 }
442 
443 #define MAX_OFFENDERS 32
444 static CmapEntry *offenders[MAX_OFFENDERS + 1];
445 static int num_offenders;
446 
447 static void
insert_in_list(CmapEntry * pCmap)448 insert_in_list(CmapEntry *pCmap)
449 {
450     int i;
451     float dE = pCmap->dE;
452 
453     for (i = num_offenders; i > 0; --i) {
454         if (dE < offenders[i-1]->dE) break;
455         offenders[i] = offenders[i-1];
456     }
457 
458     offenders[i] = pCmap;
459     if (num_offenders < MAX_OFFENDERS) ++num_offenders;
460 }
461 
462 static void
handle_biggest_offenders(int testtblsize,int maxcolors)463 handle_biggest_offenders(int testtblsize, int maxcolors) {
464     int i, j;
465     float dEthresh = 0;
466     CmapEntry *pCmap;
467 
468     num_offenders = 0;
469 
470     for (pCmap = virt_cmap, i = 0; i < num_virt_cmap_entries; i++, pCmap++) {
471         if (pCmap->nextidx < 0) {
472             continue;
473         }
474         if (num_offenders == MAX_OFFENDERS
475             && pCmap->dE < offenders[MAX_OFFENDERS-1]->dE)
476         {
477             continue;
478         }
479         find_nearest(pCmap);
480         insert_in_list(pCmap);
481     }
482 
483     if (num_offenders > 0) {
484         dEthresh = offenders[num_offenders-1]->dE;
485     }
486 
487     for (i = 0; (total < maxcolors) && (i < num_offenders); ++i) {
488         pCmap = offenders[i];
489 
490         if (!pCmap) continue;
491 
492         j = add_color(pCmap->red, pCmap->green, pCmap->blue, FALSE);
493 
494         if (j) {
495             for (j = i+1; j < num_offenders; ++j) {
496                 float dE;
497 
498                 pCmap = offenders[j];
499                 if (!pCmap) {
500                     continue;
501                 }
502 
503                 find_nearest(pCmap);
504 
505                 dE = pCmap->dE;
506                 if (dE < dEthresh) {
507                     offenders[j] = 0;
508                 } else {
509                     if (offenders[i+1] == 0 || dE > offenders[i+1]->dE) {
510                         offenders[j] = offenders[i+1];
511                         offenders[i+1] = pCmap;
512                     }
513                 }
514             }
515         }
516     }
517 }
518 
519 JNIEXPORT void JNICALL
img_makePalette(int cmapsize,int tablesize,int lookupsize,float lscale,float weight,int prevclrs,int doMac,unsigned char * reds,unsigned char * greens,unsigned char * blues,unsigned char * lookup)520 img_makePalette(int cmapsize, int tablesize, int lookupsize,
521                 float lscale, float weight,
522                 int prevclrs, int doMac,
523                 unsigned char *reds,
524                 unsigned char *greens,
525                 unsigned char *blues,
526                 unsigned char *lookup)
527 {
528     CmapEntry *pCmap;
529     int i, ix;
530 #ifdef STATS
531     double ave_dL, ave_dE;
532     double max_dL, max_dE;
533 #endif /* STATS */
534 #ifdef TIMES
535     clock_t start, mid, tbl, end;
536 
537     start = clock();
538 #endif /* TIMES */
539 
540     init_matrices();
541     Lscale = lscale;
542     Weight = weight;
543 
544     cmapmax = cmapsize;
545     total = 0;
546     for (i = 0; i < prevclrs; i++) {
547         add_color(reds[i], greens[i], blues[i], TRUE);
548     }
549 
550     add_color(0, 0, 0, TRUE);
551     add_color(255,255,255, TRUE);
552 
553     /* do grays next; otherwise find_nearest may break! */
554     init_grays();
555     if (doMac) {
556         init_mac_palette();
557     }
558     init_pastels();
559 
560     init_primaries();
561 
562     /* special case some blues */
563     add_color(0,0,192,TRUE);
564     add_color(0x30,0x20,0x80,TRUE);
565     add_color(0x20,0x60,0xc0,TRUE);
566 
567     init_virt_cmap(lookupsize, tablesize);
568 
569     while (total < cmapsize) {
570         handle_biggest_offenders(tablesize, cmapsize);
571     }
572 
573     memcpy(reds, cmap_r, cmapsize);
574     memcpy(greens, cmap_g, cmapsize);
575     memcpy(blues, cmap_b, cmapsize);
576 
577 #ifdef TIMES
578     mid = clock();
579 #endif /* TIMES */
580 
581     pCmap = virt_cmap;
582     for (i = 0; i < num_virt_cmap_entries; i++, pCmap++) {
583         if (pCmap->nextidx < 0) {
584             continue;
585         }
586         if (pCmap->nextidx < total) {
587             ix = find_nearest(pCmap);
588         }
589     }
590 
591 #ifdef TIMES
592     tbl = clock();
593 #endif /* TIMES */
594 
595     pCmap = virt_cmap;
596     if (tablesize != lookupsize) {
597         int r, g, b;
598         for (r = 0; r < lookupsize; ++r)
599         {
600             for (g = 0; g < lookupsize; ++g)
601             {
602                 for (b = 0; b < lookupsize; ++b, pCmap++)
603                 {
604                     float L, U, V;
605                     float bestd = 0;
606                     CmapEntry *pTest;
607 
608                     if (pCmap->nextidx >= 0) {
609                         continue;
610                     }
611 #ifdef DEBUG
612                     if (r == g && g == b) {
613                         jio_fprintf(stderr, "GRAY VALUE!?\n");
614                     }
615 #endif /* DEBUG */
616                     L = pCmap->L;
617                     U = pCmap->U;
618                     V = pCmap->V;
619                     for (i = 0; i < 8; i++) {
620                         int ri, gi, bi;
621                         float d, t;
622                         ri = (i & 1) ? prevtest[r] : nexttest[r];
623                         gi = (i & 2) ? prevtest[g] : nexttest[g];
624                         bi = (i & 4) ? prevtest[b] : nexttest[b];
625                         pTest = &virt_cmap[((ri * lookupsize)
626                                             + gi) * lookupsize
627                                            + bi];
628 #ifdef DEBUG
629                         if (pTest->nextidx < 0) {
630                             jio_fprintf(stderr, "OOPS!\n");
631                         }
632 #endif /* DEBUG */
633                         ix = pTest->bestidx;
634                         t = Ltab[ix] - L; d  = t * t * Lscale;
635                         if (i != 0 && d > bestd) continue;
636                         t = Utab[ix] - U; d += t * t;
637                         if (i != 0 && d > bestd) continue;
638                         t = Vtab[ix] - V; d += t * t;
639                         if (i != 0 && d > bestd) continue;
640                         bestd = d;
641                         pCmap->bestidx = ix;
642                     }
643                 }
644             }
645         }
646     }
647     pCmap = virt_cmap;
648     for (i = 0; i < num_virt_cmap_entries; i++) {
649         *lookup++ = (pCmap++)->bestidx;
650     }
651 
652 #ifdef TIMES
653     end = clock();
654 #endif /* TIMES */
655 
656 #ifdef STATS
657     max_dL = 0.0;
658     max_dE = 0.0;
659     ave_dL = 0.0;
660     ave_dE = 0.0;
661 
662     pCmap = virt_cmap;
663     for (i = 0; i < num_virt_cmap_entries; i++, pCmap++) {
664         double t, dL, dU, dV, dE;
665         if (pCmap->nextidx < 0) {
666             int ix = pCmap->bestidx;
667             dL = pCmap->L - Ltab[ix]; dL *= dL;
668             dU = pCmap->U - Utab[ix]; dU *= dU;
669             dV = pCmap->V - Vtab[ix]; dV *= dV;
670             dE = dL * Lscale + dU + dV;
671             dE = WEIGHT_DIST(dE, pCmap->L);
672         } else {
673             dL = pCmap->dL;
674             dE = pCmap->dE;
675         }
676 
677         if (dL > max_dL) max_dL = dL;
678         t = UNWEIGHT_DIST(dE,dL) - dL*(Lscale-1);
679         if (t > max_dE) max_dE = t;
680 
681         ave_dL += (dL > 0) ? sqrt(dL) : 0.0;
682         ave_dE += (t > 0) ? sqrt(t) : 0.0;
683     }
684 
685     jio_fprintf(stderr, "colors=%d, tablesize=%d, cubesize=%d, ",
686             cmapsize, tablesize, lookupsize);
687     jio_fprintf(stderr, "Lscale=%5.3f, Weight=%5.3f mac=%s\n",
688             (double)lscale, (double)weight, doMac ? "true" : "false");
689     jio_fprintf(stderr, "Worst case error dL = %5.3f, dE = %5.3f\n",
690             sqrt(max_dL), sqrt(max_dE));
691     jio_fprintf(stderr, "Average error dL = %5.3f, dE = %5.3f\n",
692             ave_dL / num_virt_cmap_entries,  ave_dE / num_virt_cmap_entries);
693 #endif /* STATS */
694 #ifdef TIMES
695     jio_fprintf(stderr, "%f seconds to find colors\n",
696             (double)(mid - start) / CLOCKS_PER_SEC);
697     jio_fprintf(stderr, "%f seconds to finish nearest colors\n",
698             (double)(tbl - mid) / CLOCKS_PER_SEC);
699     jio_fprintf(stderr, "%f seconds to make lookup table\n",
700             (double)(end - tbl) / CLOCKS_PER_SEC);
701     jio_fprintf(stderr, "%f seconds total\n",
702             (double)(end - start) / CLOCKS_PER_SEC);
703 #endif /* TIMES */
704 
705     free(virt_cmap);
706     virt_cmap = 0;
707 }
708