556 lines
15 KiB
C
556 lines
15 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
|
|
#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;
|
|
}
|