| /* |
| * Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* Iterative color palette generation */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #ifdef TIMES |
| #include <time.h> |
| #endif /* TIMES */ |
| |
| #ifndef MAKECUBE_EXE |
| #include "jvm.h" |
| #include "jni_util.h" |
| |
| extern JavaVM *jvm; |
| #endif |
| |
| #define jio_fprintf fprintf |
| |
| #define TRUE 1 |
| #define FALSE 0 |
| static float monitor_gamma[3] = {2.6f, 2.6f, 2.4f}; /* r,g,b */ |
| static float mat[3][3] = { |
| {0.3811f, 0.2073f, 0.0213f}, |
| {0.3203f, 0.6805f, 0.1430f}, |
| {0.2483f, 0.1122f, 1.2417f} |
| }; |
| static float whiteXYZ[3] = { 0.9497f, 1.0000f, 1.4060f }; |
| #define whitex (0.9497f / (0.9497f + 1.0000f + 1.4060f)) |
| #define whitey (1.0000f / (0.9497f + 1.0000f + 1.4060f)) |
| static float uwht = 4*whitex/(-2*whitex + 12*whitey + 3); |
| static float vwht = 9*whitey/(-2*whitex + 12*whitey + 3); |
| |
| static float Rmat[3][256]; |
| static float Gmat[3][256]; |
| static float Bmat[3][256]; |
| static float Ltab[256], Utab[256], Vtab[256]; |
| |
| typedef struct { |
| unsigned char red; |
| unsigned char green; |
| unsigned char blue; |
| unsigned char bestidx; |
| int nextidx; |
| float L, U, V; |
| float dist; |
| float dE; |
| float dL; |
| } CmapEntry; |
| |
| static int num_virt_cmap_entries; |
| static CmapEntry *virt_cmap; |
| static int prevtest[256]; |
| static int nexttest[256]; |
| |
| static float Lscale = 10.0f; |
| /* this is a multiplier--it should not be zero */ |
| static float Weight = 250.0f; |
| |
| #define WEIGHT_DIST(d,l) (Weight*(d)/(Weight+(l))) |
| #define UNWEIGHT_DIST(d,l) ((Weight+(l))*(d)/Weight) |
| |
| #if 0 |
| #define WEIGHT_DIST(d,l) (d) |
| #define UNWEIGHT_DIST(d,l) (d) |
| #endif |
| |
| static void |
| init_matrices() |
| { |
| static int done = 0; |
| int i; |
| |
| if (done) { |
| return; |
| } |
| for (i = 0; i < 256; ++i) |
| { |
| float iG = (float) pow(i/255.0, monitor_gamma[0]); |
| Rmat[0][i] = mat[0][0] * iG; |
| Rmat[1][i] = mat[0][1] * iG; |
| Rmat[2][i] = mat[0][2] * iG; |
| |
| iG = (float) pow(i/255.0, monitor_gamma[1]); |
| Gmat[0][i] = mat[1][0] * iG; |
| Gmat[1][i] = mat[1][1] * iG; |
| Gmat[2][i] = mat[1][2] * iG; |
| |
| iG = (float) pow(i/255.0, monitor_gamma[2]); |
| Bmat[0][i] = mat[2][0] * iG; |
| Bmat[1][i] = mat[2][1] * iG; |
| Bmat[2][i] = mat[2][2] * iG; |
| } |
| done = 1; |
| } |
| |
| static void |
| LUV_convert(int red, int grn, int blu, float *L, float *u, float *v) |
| { |
| float X = Rmat[0][red] + Gmat[0][grn] + Bmat[0][blu]; |
| float Y = Rmat[1][red] + Gmat[1][grn] + Bmat[1][blu]; |
| float Z = Rmat[2][red] + Gmat[2][grn] + Bmat[2][blu]; |
| float sum = X+Y+Z; |
| |
| if (sum != 0.0f) { |
| float x = X/sum; |
| float y = Y/sum; |
| float dnm = -2*x + 12*y + 3; |
| float ytmp = (float) pow(Y/whiteXYZ[1], 1.0/3.0); |
| |
| if (ytmp < .206893f) { |
| *L = 903.3f*Y/whiteXYZ[1]; |
| } else { |
| *L = 116*(ytmp) - 16; |
| } |
| if (dnm != 0.0f) { |
| float uprm = 4*x/dnm; |
| float vprm = 9*y/dnm; |
| |
| *u = 13*(*L)*(uprm-uwht); |
| *v = 13*(*L)*(vprm-vwht); |
| } else { |
| *u = 0.0f; |
| *v = 0.0f; |
| } |
| } else { |
| *L = 0.0f; |
| *u = 0.0f; |
| *v = 0.0f; |
| } |
| } |
| |
| static int cmapmax; |
| static int total; |
| static unsigned char cmap_r[256], cmap_g[256], cmap_b[256]; |
| |
| #define DIST_THRESHOLD 7 |
| static int |
| no_close_color(float l, float u, float v, int c_tot, int exact) { |
| int i; |
| for (i = 0; i < c_tot; ++i) { |
| float t, dist = 0.0f; |
| t = Ltab[i] - l; dist += t*t*Lscale; |
| t = Utab[i] - u; dist += t*t; |
| t = Vtab[i] - v; dist += t*t; |
| |
| if (dist < (exact ? 0.1 : DIST_THRESHOLD)) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| add_color(int r, int g, int b, int f) { |
| if (total >= cmapmax) |
| return 0; |
| cmap_r[total] = r; |
| cmap_g[total] = g; |
| cmap_b[total] = b; |
| LUV_convert(cmap_r[total],cmap_g[total],cmap_b[total], |
| Ltab + total, Utab + total, Vtab + total); |
| if (no_close_color(Ltab[total], Utab[total], Vtab[total], total-1, f)) { |
| ++total; |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void |
| init_primaries() { |
| int r, g, b; |
| |
| for (r = 0; r < 256; r += (r?128:127)) { |
| for (g = 0; g < 256; g += (g?128:127)) { |
| for (b = 0; b < 256; b += (b?128:127)) { |
| if ((r == g) && (g == b)) continue; /* black or white */ |
| add_color(r, g, b, TRUE); |
| } |
| } |
| } |
| } |
| |
| static void |
| init_pastels() { |
| int i; |
| /* very light colors */ |
| for (i = 6; i >= 0; --i) |
| add_color((i&4) ? 0xff : 0xf0, |
| (i&2) ? 0xff : 0xf0, |
| (i&1) ? 0xff : 0xf0, TRUE); |
| } |
| |
| static void |
| init_grays() { |
| int i; |
| for (i = 15; i < 255; i += 16) |
| add_color(i, i, i, TRUE); |
| } |
| |
| static void |
| init_mac_palette() { |
| add_color(255, 255, 204, TRUE); |
| add_color(255, 255, 0, TRUE); |
| add_color(255, 204, 153, TRUE); |
| add_color(255, 102, 204, TRUE); |
| add_color(255, 102, 51, TRUE); |
| add_color(221, 0, 0, TRUE); |
| add_color(204, 204, 255, TRUE); |
| add_color(204, 153, 102, TRUE); |
| add_color(153, 255, 255, TRUE); |
| add_color(153, 153, 255, TRUE); |
| add_color(153, 102, 153, TRUE); |
| add_color(153, 0, 102, TRUE); |
| add_color(102, 102, 204, TRUE); |
| add_color(51, 255, 153, TRUE); |
| add_color(51, 153, 102, TRUE); |
| add_color(51, 102, 102, TRUE); |
| add_color(51, 51, 102, TRUE); |
| add_color(51, 0, 153, TRUE); |
| add_color(0, 187, 0, TRUE); |
| add_color(0, 153, 255, TRUE); |
| add_color(0, 0, 221, TRUE); |
| } |
| |
| static void |
| init_virt_cmap(int tablesize, int testsize) |
| { |
| int r, g, b; |
| int gray = -1; |
| CmapEntry *pCmap; |
| unsigned int dotest[256]; |
| |
| if (virt_cmap) { |
| free(virt_cmap); |
| virt_cmap = 0; |
| } |
| |
| num_virt_cmap_entries = tablesize * tablesize * tablesize; |
| virt_cmap = malloc(sizeof(CmapEntry) * num_virt_cmap_entries); |
| /* |
| * Fix for bug 4070647 malloc return value not check in img_colors.c |
| * We have to handle the malloc failure differently under |
| * Win32 and Solaris since under Solaris this file is linked with |
| * libawt.so and under Win32 it's a separate awt_makecube.exe |
| * application. |
| */ |
| if (virt_cmap == NULL) { |
| #ifndef MAKECUBE_EXE |
| JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| JNU_ThrowOutOfMemoryError(env, "init_virt_cmap: OutOfMemoryError"); |
| return; |
| #else |
| fprintf(stderr,"init_virt_cmap: OutOfMemoryError\n"); |
| exit(-1); |
| #endif |
| } |
| pCmap = virt_cmap; |
| for (r = 0; r < total; r++) { |
| if (cmap_r[r] == cmap_g[r] && cmap_g[r] == cmap_b[r]) { |
| if (gray < 0 || cmap_r[gray] < cmap_r[r]) { |
| gray = r; |
| } |
| } |
| } |
| if (gray < 0) { |
| #ifdef DEBUG |
| jio_fprintf(stderr, "Didn't find any grays in color table!\n"); |
| #endif /* DEBUG */ |
| gray = 0; |
| } |
| g = 0; |
| b = 0; |
| for (r = 0; r < tablesize - 1; ++r) { |
| if (g >= 0) { |
| b = r; |
| dotest[r] = 1; |
| g -= tablesize; |
| } else { |
| dotest[r] = 0; |
| } |
| prevtest[r] = b; |
| g += testsize; |
| } |
| b = r; |
| prevtest[r] = b; |
| dotest[r] = 1; |
| for (r = tablesize - 1; r >= 0; --r) { |
| if (prevtest[r] == r) { |
| b = r; |
| } |
| nexttest[r] = b; |
| } |
| #ifdef DEBUG |
| for (r = 0; r < tablesize; ++r) { |
| if (dotest[r]) { |
| if (prevtest[r] != r || nexttest[r] != r) { |
| jio_fprintf(stderr, "prev/next != r!\n"); |
| } |
| } |
| } |
| #endif /* DEBUG */ |
| for (r = 0; r < tablesize; ++r) |
| { |
| int red = (int)(floor(r*255.0/(tablesize - 1))); |
| for (g = 0; g < tablesize; ++g) |
| { |
| int green = (int)(floor(g*255.0/(tablesize - 1))); |
| for (b = 0; b < tablesize; ++b) |
| { |
| int blue = (int)(floor(b*255.0/(tablesize - 1))); |
| float t, d; |
| if (pCmap >= virt_cmap + num_virt_cmap_entries) { |
| #ifdef DEBUG |
| jio_fprintf(stderr, "OUT OF pCmap CONVERSION SPACE!\n"); |
| #endif /* DEBUG */ |
| continue; /* Shouldn't happen */ |
| } |
| pCmap->red = red; |
| pCmap->green = green; |
| pCmap->blue = blue; |
| LUV_convert(red, green, blue, &pCmap->L, &pCmap->U, &pCmap->V); |
| if ((red != green || green != blue) && |
| (!dotest[r] || !dotest[g] || !dotest[b])) |
| { |
| pCmap->nextidx = -1; |
| pCmap++; |
| continue; |
| } |
| pCmap->bestidx = gray; |
| pCmap->nextidx = 0; |
| t = Ltab[gray] - pCmap->L; |
| d = t * t; |
| if (red == green && green == blue) { |
| pCmap->dist = d; |
| d *= Lscale; |
| } else { |
| d *= Lscale; |
| t = Utab[gray] - pCmap->U; |
| d += t * t; |
| t = Vtab[gray] - pCmap->V; |
| d += t * t; |
| pCmap->dist = d; |
| } |
| pCmap->dE = WEIGHT_DIST(d, pCmap->L); |
| pCmap++; |
| } |
| } |
| } |
| #ifdef DEBUG |
| if (pCmap < virt_cmap + num_virt_cmap_entries) { |
| jio_fprintf(stderr, "Didn't fill pCmap conversion table!\n"); |
| } |
| #endif /* DEBUG */ |
| } |
| |
| static int |
| find_nearest(CmapEntry *pCmap) { |
| int red = pCmap->red; |
| int grn = pCmap->green; |
| int blu = pCmap->blue; |
| float L = pCmap->L; |
| float dist; |
| int i; |
| |
| if ((red == grn) && (grn == blu)) { |
| dist = pCmap->dist; |
| |
| for (i = pCmap->nextidx; i < total; ++i) { |
| float dL; |
| |
| if (cmap_r[i] != cmap_g[i] || cmap_g[i] != cmap_b[i]) { |
| continue; |
| } |
| |
| dL = Ltab[i] - L; dL *= dL; |
| |
| if (dL < dist) { |
| dist = dL; |
| pCmap->dist = dist; |
| pCmap->dL = dist; |
| pCmap->dE = WEIGHT_DIST(dist*Lscale,L); |
| pCmap->bestidx = i; |
| } |
| } |
| pCmap->nextidx = total; |
| } else { |
| float U = pCmap->U; |
| float V = pCmap->V; |
| dist = pCmap->dist; |
| |
| for (i = pCmap->nextidx; i < total; ++i) { |
| float dL, dU, dV, dE; |
| dL = Ltab[i] - L; dL *= (dL*Lscale); |
| dU = Utab[i] - U; dU *= dU; |
| dV = Vtab[i] - V; dV *= dV; |
| |
| dE = dL + dU + dV; |
| if (dE < dist) |
| { |
| dist = dE; |
| /* *delta = (dL/4) + dU + dV; */ |
| /* *delta = dist */ |
| /* *delta = dL + 100*(dU+dV)/(100+L); */ |
| pCmap->dist = dist; |
| pCmap->dE = WEIGHT_DIST(dE, L); |
| pCmap->dL = dL/Lscale; |
| pCmap->bestidx = i; |
| } |
| } |
| pCmap->nextidx = total; |
| } |
| |
| return pCmap->bestidx; |
| } |
| |
| #define MAX_OFFENDERS 32 |
| static CmapEntry *offenders[MAX_OFFENDERS + 1]; |
| static int num_offenders; |
| |
| static void |
| insert_in_list(CmapEntry *pCmap) |
| { |
| int i; |
| float dE = pCmap->dE; |
| |
| for (i = num_offenders; i > 0; --i) { |
| if (dE < offenders[i-1]->dE) break; |
| offenders[i] = offenders[i-1]; |
| } |
| |
| offenders[i] = pCmap; |
| if (num_offenders < MAX_OFFENDERS) ++num_offenders; |
| } |
| |
| static void |
| handle_biggest_offenders(int testtblsize, int maxcolors) { |
| int i, j; |
| float dEthresh = 0; |
| CmapEntry *pCmap; |
| |
| num_offenders = 0; |
| |
| for (pCmap = virt_cmap, i = 0; i < num_virt_cmap_entries; i++, pCmap++) { |
| if (pCmap->nextidx < 0) { |
| continue; |
| } |
| if (num_offenders == MAX_OFFENDERS |
| && pCmap->dE < offenders[MAX_OFFENDERS-1]->dE) |
| { |
| continue; |
| } |
| find_nearest(pCmap); |
| insert_in_list(pCmap); |
| } |
| |
| if (num_offenders > 0) { |
| dEthresh = offenders[num_offenders-1]->dE; |
| } |
| |
| for (i = 0; (total < maxcolors) && (i < num_offenders); ++i) { |
| pCmap = offenders[i]; |
| |
| if (!pCmap) continue; |
| |
| j = add_color(pCmap->red, pCmap->green, pCmap->blue, FALSE); |
| |
| if (j) { |
| for (j = i+1; j < num_offenders; ++j) { |
| float dE; |
| |
| pCmap = offenders[j]; |
| if (!pCmap) { |
| continue; |
| } |
| |
| find_nearest(pCmap); |
| |
| dE = pCmap->dE; |
| if (dE < dEthresh) { |
| offenders[j] = 0; |
| } else { |
| if (offenders[i+1] == 0 || dE > offenders[i+1]->dE) { |
| offenders[j] = offenders[i+1]; |
| offenders[i+1] = pCmap; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void |
| 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) |
| { |
| CmapEntry *pCmap; |
| int i, ix; |
| #ifdef STATS |
| double ave_dL, ave_dE; |
| double max_dL, max_dE; |
| #endif /* STATS */ |
| #ifdef TIMES |
| clock_t start, mid, tbl, end; |
| |
| start = clock(); |
| #endif /* TIMES */ |
| |
| init_matrices(); |
| Lscale = lscale; |
| Weight = weight; |
| |
| cmapmax = cmapsize; |
| total = 0; |
| for (i = 0; i < prevclrs; i++) { |
| add_color(reds[i], greens[i], blues[i], TRUE); |
| } |
| |
| add_color(0, 0, 0, TRUE); |
| add_color(255,255,255, TRUE); |
| |
| /* do grays next; otherwise find_nearest may break! */ |
| init_grays(); |
| if (doMac) { |
| init_mac_palette(); |
| } |
| init_pastels(); |
| |
| init_primaries(); |
| |
| /* special case some blues */ |
| add_color(0,0,192,TRUE); |
| add_color(0x30,0x20,0x80,TRUE); |
| add_color(0x20,0x60,0xc0,TRUE); |
| |
| init_virt_cmap(lookupsize, tablesize); |
| |
| while (total < cmapsize) { |
| handle_biggest_offenders(tablesize, cmapsize); |
| } |
| |
| memcpy(reds, cmap_r, cmapsize); |
| memcpy(greens, cmap_g, cmapsize); |
| memcpy(blues, cmap_b, cmapsize); |
| |
| #ifdef TIMES |
| mid = clock(); |
| #endif /* TIMES */ |
| |
| pCmap = virt_cmap; |
| for (i = 0; i < num_virt_cmap_entries; i++, pCmap++) { |
| if (pCmap->nextidx < 0) { |
| continue; |
| } |
| if (pCmap->nextidx < total) { |
| ix = find_nearest(pCmap); |
| } |
| } |
| |
| #ifdef TIMES |
| tbl = clock(); |
| #endif /* TIMES */ |
| |
| pCmap = virt_cmap; |
| if (tablesize != lookupsize) { |
| int r, g, b; |
| for (r = 0; r < lookupsize; ++r) |
| { |
| for (g = 0; g < lookupsize; ++g) |
| { |
| for (b = 0; b < lookupsize; ++b, pCmap++) |
| { |
| float L, U, V; |
| float bestd = 0; |
| CmapEntry *pTest; |
| |
| if (pCmap->nextidx >= 0) { |
| continue; |
| } |
| #ifdef DEBUG |
| if (r == g && g == b) { |
| jio_fprintf(stderr, "GRAY VALUE!?\n"); |
| } |
| #endif /* DEBUG */ |
| L = pCmap->L; |
| U = pCmap->U; |
| V = pCmap->V; |
| for (i = 0; i < 8; i++) { |
| int ri, gi, bi; |
| float d, t; |
| ri = (i & 1) ? prevtest[r] : nexttest[r]; |
| gi = (i & 2) ? prevtest[g] : nexttest[g]; |
| bi = (i & 4) ? prevtest[b] : nexttest[b]; |
| pTest = &virt_cmap[((ri * lookupsize) |
| + gi) * lookupsize |
| + bi]; |
| #ifdef DEBUG |
| if (pTest->nextidx < 0) { |
| jio_fprintf(stderr, "OOPS!\n"); |
| } |
| #endif /* DEBUG */ |
| ix = pTest->bestidx; |
| t = Ltab[ix] - L; d = t * t * Lscale; |
| if (i != 0 && d > bestd) continue; |
| t = Utab[ix] - U; d += t * t; |
| if (i != 0 && d > bestd) continue; |
| t = Vtab[ix] - V; d += t * t; |
| if (i != 0 && d > bestd) continue; |
| bestd = d; |
| pCmap->bestidx = ix; |
| } |
| } |
| } |
| } |
| } |
| pCmap = virt_cmap; |
| for (i = 0; i < num_virt_cmap_entries; i++) { |
| *lookup++ = (pCmap++)->bestidx; |
| } |
| |
| #ifdef TIMES |
| end = clock(); |
| #endif /* TIMES */ |
| |
| #ifdef STATS |
| max_dL = 0.0; |
| max_dE = 0.0; |
| ave_dL = 0.0; |
| ave_dE = 0.0; |
| |
| pCmap = virt_cmap; |
| for (i = 0; i < num_virt_cmap_entries; i++, pCmap++) { |
| double t, dL, dU, dV, dE; |
| if (pCmap->nextidx < 0) { |
| int ix = pCmap->bestidx; |
| dL = pCmap->L - Ltab[ix]; dL *= dL; |
| dU = pCmap->U - Utab[ix]; dU *= dU; |
| dV = pCmap->V - Vtab[ix]; dV *= dV; |
| dE = dL * Lscale + dU + dV; |
| dE = WEIGHT_DIST(dE, pCmap->L); |
| } else { |
| dL = pCmap->dL; |
| dE = pCmap->dE; |
| } |
| |
| if (dL > max_dL) max_dL = dL; |
| t = UNWEIGHT_DIST(dE,dL) - dL*(Lscale-1); |
| if (t > max_dE) max_dE = t; |
| |
| ave_dL += (dL > 0) ? sqrt(dL) : 0.0; |
| ave_dE += (t > 0) ? sqrt(t) : 0.0; |
| } |
| |
| jio_fprintf(stderr, "colors=%d, tablesize=%d, cubesize=%d, ", |
| cmapsize, tablesize, lookupsize); |
| jio_fprintf(stderr, "Lscale=%5.3f, Weight=%5.3f mac=%s\n", |
| (double)lscale, (double)weight, doMac ? "true" : "false"); |
| jio_fprintf(stderr, "Worst case error dL = %5.3f, dE = %5.3f\n", |
| sqrt(max_dL), sqrt(max_dE)); |
| jio_fprintf(stderr, "Average error dL = %5.3f, dE = %5.3f\n", |
| ave_dL / num_virt_cmap_entries, ave_dE / num_virt_cmap_entries); |
| #endif /* STATS */ |
| #ifdef TIMES |
| jio_fprintf(stderr, "%f seconds to find colors\n", |
| (double)(mid - start) / CLOCKS_PER_SEC); |
| jio_fprintf(stderr, "%f seconds to finish nearest colors\n", |
| (double)(tbl - mid) / CLOCKS_PER_SEC); |
| jio_fprintf(stderr, "%f seconds to make lookup table\n", |
| (double)(end - tbl) / CLOCKS_PER_SEC); |
| jio_fprintf(stderr, "%f seconds total\n", |
| (double)(end - start) / CLOCKS_PER_SEC); |
| #endif /* TIMES */ |
| |
| free(virt_cmap); |
| virt_cmap = 0; |
| } |