/* * xcolorcube.c - Manage color allocation on X display * * (c) Copyright 1995 Erik Corry ehcorry@inet.uni-c.dk erik@kroete2.freinet.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "port_before.h" #include #include #include #include #include "port_after.h" #include "colorcube.h" #include "xcolorcube.h" typedef struct ls_cubealist { Display *display; Colormap colormap; cct_cube cube; struct ls_cubealist *next; } *lt_cubealist; static lt_cubealist lv_cubealist = 0; static lt_cubealist lv_grayalist = 0; static bool lf_attempt_axbxc( cct_cube cube, Display *display, Colormap colormap, int a, int b, int c) { unsigned long allocated[256]; int la, lb, lc; int n = 0; for(la = 0; la < a; la++) { cube->u.mapping.pixel_values[0][la] = b*c*la; for(lb = 0; lb < b; lb++) { cube->u.mapping.pixel_values[1][lb] = c*lb; for(lc = 0; lc < c; lc++) { XColor try; int status; cube->u.mapping.pixel_values[2][lc] = lc; try.red = (unsigned short) (0.001+(65535.0 * la) / (double) (a - 1)); try.green = (unsigned short) (0.001+(65535.0 * lb) / (double) (b - 1)); try.blue = (unsigned short) (0.001+(65535.0 * lc) / (double) (c - 1)); cube->u.mapping.brightnesses[0][la] = try.red >> 8; cube->u.mapping.brightnesses[1][lb] = try.green >> 8; cube->u.mapping.brightnesses[2][lc] = try.blue >> 8; try.flags = DoRed | DoGreen | DoBlue; status = XAllocColor(display, colormap, &try); if(!status && n) { XFreeColors(display, colormap, allocated, n, 0); return(false); } cube->u.mapping.mapping[n] = try.pixel; allocated[n] = try.pixel; n++; } } } /* TODO: test whether mapping is really necessary */ cube->cube_type = cube_mapping; cube->u.mapping.value_count[0] = a; cube->u.mapping.value_count[1] = b; cube->u.mapping.value_count[2] = c; if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) printf("%dx%dx%d color cube allocated\n", a, b, c); return true; } cct_cube xccf_allocate_cube( Display *display, Colormap colormap, Visual *visual, int depth) { cct_cube cube; lt_cubealist liststepper; lt_cubealist *listextension; for(liststepper = lv_cubealist, listextension = &lv_cubealist; liststepper; listextension = &(liststepper->next), liststepper = liststepper->next) { if(liststepper->display == display && liststepper->colormap == colormap) return liststepper->cube; } *listextension = (lt_cubealist)calloc(1, sizeof(struct ls_cubealist)); (*listextension)->display = display; (*listextension)->colormap = colormap; cube = (cct_cube)calloc(1, sizeof(struct ccs_cube)); (*listextension)->cube = cube; if(visual->class == TrueColor) { cube->cube_type = cube_true_color; cube->u.true_color.color_mask[0] = visual->red_mask; cube->u.true_color.color_mask[1] = visual->green_mask; cube->u.true_color.color_mask[2] = visual->blue_mask; return cube; } else if(visual->class == StaticGray || visual->class == GrayScale) goto failure; /* else PseudoColor (or StaticColor or DirectColor: do they exist?) */ if(visual->class == PseudoColor && visual->map_entries < 8) goto failure; /* TODO get a standard colormap (if there is one) from the Xmu routines */ if(depth > 8 || getenv("CHIMERA_AGGRESSIVE_COLORS")) { if(lf_attempt_axbxc(cube, display, colormap, 6, 10, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 7, 9, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 6, 9, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 6, 8, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 6, 8, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 5, 10, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 5, 9, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 4, 10, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 4, 9, 4)) return cube; } if(depth == 8) { /* share with xv */ if(lf_attempt_axbxc(cube, display, colormap, 4, 8, 4)) return cube; /* share with ghostscript */ if(lf_attempt_axbxc(cube, display, colormap, 5, 5, 5)) return cube; /* share with old versions of xv and N******e */ if(lf_attempt_axbxc(cube, display, colormap, 6, 6, 6)) return cube; /* share with N*******e -ncols 64 */ if(lf_attempt_axbxc(cube, display, colormap, 4, 4, 4)) return cube; /* getting desperate */ if(lf_attempt_axbxc(cube, display, colormap, 4, 6, 4)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 3, 5, 3)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 3, 4, 3)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 3, 3, 3)) return cube; if(lf_attempt_axbxc(cube, display, colormap, 2, 5, 2)) return cube; } if(lf_attempt_axbxc(cube, display, colormap, 2, 4, 2)) return cube; /* almost total desperation */ if(lf_attempt_axbxc(cube, display, colormap, 2, 3, 2)) return cube; /* total desperation */ if(lf_attempt_axbxc(cube, display, colormap, 2, 2, 2)) return cube; failure: free(cube); (*listextension)->cube = 0; return 0; } static bool lf_get_gray( Display *display, Colormap colormap, cct_cube cube, int n) { XColor try; int status; try.red = cube->u.grayscale.brightnesses[n] << 8; try.green = cube->u.grayscale.brightnesses[n] << 8; try.blue = cube->u.grayscale.brightnesses[n] << 8; try.flags = DoRed | DoGreen | DoBlue; status = XAllocColor(display, colormap, &try); if(!status) return false; cube->u.grayscale.pixel_values[n] = try.pixel; cube->u.grayscale.brightnesses[n] = try.green >> 8; return true; } static int lf_reverse_byte(int a) { return ((a & 128) >> 7) | ((a & 64) >> 5) | ((a & 32) >> 3) | ((a & 16) >> 1) | ((a & 8) << 1) | ((a & 4) << 3) | ((a & 2) << 5) | ((a & 1) << 7); } /* * The Window parameter is only needed to allow the Visual to be * determined */ cct_cube xccf_allocate_grays( Display *display, Colormap colormap, Visual *visual, int depth) { cct_cube cube; lt_cubealist liststepper; lt_cubealist *listextension; int n = 0; int maxcols, i, j; /* * Make sure that the color cube is always allocated before the gray * colormap entries */ xccf_allocate_cube(display, colormap, visual, depth); for(liststepper = lv_grayalist, listextension = &lv_grayalist; liststepper; listextension = &(liststepper->next), liststepper = liststepper->next) { if(liststepper->display == display && liststepper->colormap == colormap) return liststepper->cube; } (*listextension) = (lt_cubealist)calloc(1, sizeof(struct ls_cubealist)); (*listextension)->display = display; (*listextension)->colormap = colormap; cube = (cct_cube)calloc(1, sizeof(struct ccs_cube)); (*listextension)->cube = cube; /* * We are really saving grays here. With only 16 grays we will dither, not * just remap. */ if(visual->class != PseudoColor || depth > 8) maxcols = 256; else maxcols = 16; if(visual->class == PseudoColor && visual->map_entries < 8) maxcols = 3; /* TODO get a standard colormap (if there is one) from the Xmu routines */ cube->u.grayscale.brightnesses[0] = 0; cube->u.grayscale.brightnesses[1] = 255; if(!lf_get_gray(display, colormap, cube, 0)) goto failure; n = 1; if(!lf_get_gray(display, colormap, cube, 1)) goto failure; n = 2; for(i = 1; i < 255 && n < maxcols; i++, n++) { cube->u.grayscale.brightnesses[n] = lf_reverse_byte(i); if(!lf_get_gray(display, colormap, cube, n)) { n--; } } cube->u.grayscale.value_count = n; if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) printf("Allocated %d grays\n", n); /* * Bubble sort (I know...) */ { bool something_happened; do { something_happened = false; for(i = 1; i < n; i++) { if(cube->u.grayscale.brightnesses[i] < cube->u.grayscale.brightnesses[i-1]) { unsigned long t; t = cube->u.grayscale.brightnesses[i]; cube->u.grayscale.brightnesses[i] = cube->u.grayscale.brightnesses[i-1]; cube->u.grayscale.brightnesses[i-1] = t; t = cube->u.grayscale.pixel_values[i]; cube->u.grayscale.pixel_values[i] = cube->u.grayscale.pixel_values[i-1]; cube->u.grayscale.pixel_values[i-1] = t; something_happened = true; } } for(i = n-1; i; i--) { if(cube->u.grayscale.brightnesses[i] < cube->u.grayscale.brightnesses[i-1]) { unsigned long t; t = cube->u.grayscale.brightnesses[i]; cube->u.grayscale.brightnesses[i] = cube->u.grayscale.brightnesses[i-1]; cube->u.grayscale.brightnesses[i-1] = t; t = cube->u.grayscale.pixel_values[i]; cube->u.grayscale.pixel_values[i] = cube->u.grayscale.pixel_values[i-1]; cube->u.grayscale.pixel_values[i-1] = t; something_happened = true; } } } while(something_happened); } /* * Remove duplicates */ for(j = i = 0; i < n; i++) { if(cube->u.grayscale.brightnesses[i] != cube->u.grayscale.brightnesses[j]) { j++; } cube->u.grayscale.brightnesses[j] = cube->u.grayscale.brightnesses[i]; cube->u.grayscale.pixel_values[j] = cube->u.grayscale.pixel_values[i]; } n = j + 1; cube->u.grayscale.value_count = n; if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) printf("That was %d unique grays\n", n); return cube; failure: if(n) { XFreeColors(display, colormap, cube->u.grayscale.pixel_values, n, 0); } free(cube); (*listextension)->cube = 0; return 0; } static int lf_abs(int a) { if(a >= 0) return a; return -a; } static int lf_get_color_index( unsigned short xp_intensity, unsigned int *brightnesses, int value_count) { int i; int intensity = xp_intensity >> 8; int answer = 0; int best_brightness = intensity; for (i = 0; i < value_count; i++) { int bs = brightnesses[i]; if(lf_abs(bs - intensity) < best_brightness) { best_brightness = lf_abs(bs - intensity); answer = i; } } return answer; } #ifdef __GNUC__ __inline__ #endif bool xccf_color_compare( int r1, int g1, int b1, int r2, int g2, int b2) { if(lf_abs(r1 - r2) > 6) return false; if(lf_abs(g1 - g2) > 3) return false; if(lf_abs(b1 - b2) > 6) return false; return true; } bool xccf_allocate_special( Display *display, Colormap colormap, cct_cube cube, cct_cube grayscale, unsigned short intensities[3], bool *really_allocated_return, bool dont_really_allocate, cct_special_color specials, int special_count, unsigned long *special_return) { int i; int r = intensities[0] >> 8; int g = intensities[1] >> 8; int b = intensities[2] >> 8; int gr; if(really_allocated_return) *really_allocated_return = false; if(cube) { switch(cube->cube_type) { case cube_mapping: case cube_no_mapping: { int color_index[3]; for(i = 0; i < 3; i++) color_index[i] = lf_get_color_index(intensities[i], cube->u.no_mapping.brightnesses[i], cube->u.no_mapping.value_count[i]); if(xccf_color_compare( cube->u.no_mapping.brightnesses[0][color_index[0]], cube->u.no_mapping.brightnesses[1][color_index[1]], cube->u.no_mapping.brightnesses[2][color_index[2]], r, g, b)) { *special_return = cube->u.no_mapping.pixel_values[0][color_index[0]] + cube->u.no_mapping.pixel_values[1][color_index[1]] + cube->u.no_mapping.pixel_values[2][color_index[2]]; if(cube->cube_type == cube_mapping) *special_return = cube->u.mapping.mapping[*special_return]; return true; } break; } case cube_true_color: case cube_grayscale: break; } } gr = (r + g + b) / 3; if(grayscale /* && xccf_color_compare(r,g,b,gr,gr,gr)*/) { for(i = 0; i < grayscale->u.grayscale.value_count; i++) { if(xccf_color_compare(grayscale->u.grayscale.brightnesses[i], grayscale->u.grayscale.brightnesses[i], grayscale->u.grayscale.brightnesses[i], r, g, b)) { *special_return = grayscale->u.grayscale.pixel_values[i]; return true; } } } for(i = 0; i < special_count; i++) { if(xccf_color_compare(specials[i].brightnesses[0], specials[i].brightnesses[1], specials[i].brightnesses[2], r, g, b)) { *special_return = specials[i].pixel; return true; } } if(!dont_really_allocate) { XColor try; int status; try.red = intensities[0]; try.green = intensities[1]; try.blue = intensities[2]; try.flags = DoRed | DoGreen | DoBlue; status = XAllocColor(display, colormap, &try); if(status) { if(!xccf_color_compare( intensities[0] >> 8, intensities[1] >> 8, intensities[2] >> 8, try.red >> 8, try.green >> 8, try.blue >> 8)) { /* The server gave us a color but it was a really bad match */ XFreeColors(display, colormap, &try.pixel, 1, 0); } else { /* The server gave us a color that worked. */ *special_return = try.pixel; specials[special_count].pixel = try.pixel; specials[special_count].brightnesses[0] = try.red >> 8; specials[special_count].brightnesses[1] = try.green >> 8; specials[special_count].brightnesses[2] = try.blue >> 8; if(really_allocated_return) *really_allocated_return = true; return true; } } } return false; }