/*
* @(#)color.c 1.14 95/11/27 Arthur van Hoff, Patrick Naughton
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*-
* Image dithering and rendering code for X11.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "color.h"
#include "debug.h"
#include "java_awt_Color.h"
/* Constraint on the remaining number of colors that can be allocated
for PseudoColor visuals */
static unsigned int remaining_colors = (unsigned int)-1;
static unsigned int GetMaxColors(void) {
return (unsigned int)-1;
}
unsigned int (*awt_GetMaxColors)(void) = GetMaxColors;
#define CLIP(val,min,max) ((val < min) ? min : ((val > max) ? max : val))
/* efficiently map an RGB value to a pixel value */
#define RGBMAP(r, g, b) \
(awt_RGBCube[(int)((r) + CMAP_BIAS) >> (int)(8 - CMAP_BITS)] \
[(int)((g) + CMAP_BIAS) >> (int)(8 - CMAP_BITS)] \
[(int)((b) + CMAP_BIAS) >> (int)(8 - CMAP_BITS)])
#define COMPOSETC(I, r, g, b) \
((((b) & 0xff) << ((I)->bOff)) | \
(((g) & 0xff) << ((I)->gOff)) | \
(((r) & 0xff) << ((I)->rOff)))
#define RGBTOGRAY(r, g, b) ((int) (.299 * r + .587 * g + .114 * b))
enum {
FREE_COLOR = 0,
LIKELY_COLOR = 1,
ALLOCATED_COLOR = 2
};
int awt_num_colors;
ColorEntry awt_Colors[256];
/* the color cube */
unsigned char awt_RGBCube[CMAP_COLS + 1][CMAP_COLS + 1][CMAP_COLS + 1];
/* the StaticGray ramp */
unsigned char grays[256];
unsigned char bwgamma[256];
/* the function pointers for doing image manipulation */
awtImageData *awtImage;
int awt_color_match(int, int, int);
#if 0
int awt_color_match24(int, int, int);
#endif
int awt_color_matchTC(int, int, int);
int awt_color_matchGS(int, int, int);
awtImageData awtImage8 = {
8,
8,
0, 0, 0,
0, 0, 0,
{ 0 },
{ 8, 8, 8 },
awt_color_match
};
#if 0
awtImageData awtImage24 = {
24,
32,
0, 8, 16,
{ 0 },
{ 8, 8, 8 },
awt_color_match24
};
#endif
awtImageData awtImageTC = {
0, /* these need to be computed as we go... */
32, /* although who uses this one? (awtBitsPerPixel) */
0, 0, 0,
0, 0, 0,
{ 0 },
{ 8, 8, 8 },
awt_color_matchTC
};
awtImageData awtImageGS = {
8,
8,
-1, -1, -1,
0, 0, 0, /* not used */
{ 0 },
{ 8, 8, 8 },
awt_color_matchGS
};
#ifdef DEBUG
static int debug_colormap = 0;
#endif
/*
* Find the best color.
*/
#if 0
int
awt_color_match24(int r, int g, int b)
{
r = CLIP(r, 0, 255);
g = CLIP(g, 0, 255);
b = CLIP(b, 0, 255);
return COMPOSE24(awtImage, r, g, b);
}
#endif
int
awt_color_matchTC(int r, int g, int b)
{
r = (r&255) >> awtImage->rChop;
g = (g&255) >> awtImage->gChop;
b = (b&255) >> awtImage->bChop;
return COMPOSETC(awtImage, r, g, b);
}
int
awt_color_matchGS(int r, int g, int b)
{
r = CLIP(r, 0, 255);
g = CLIP(g, 0, 255);
b = CLIP(b, 0, 255);
return grays[RGBTOGRAY(r, g, b)];
}
int
awt_color_match(int r, int g, int b)
{
int mindist = 256 * 256 * 256, besti = 0;
int i, t, d;
ColorEntry *p = awt_Colors;
r = CLIP(r, 0, 255);
g = CLIP(g, 0, 255);
b = CLIP(b, 0, 255);
for (i = 0 ; i < awt_num_colors ; i++, p++)
if (p->flags == ALLOCATED_COLOR) {
t = p->r - r;
d = t * t;
if (d >= mindist)
continue;
t = p->g - g;
d += t * t;
if (d >= mindist)
continue;
t = p->b - b;
d += t * t;
if (d >= mindist)
continue;
if (d == 0)
return i;
if (d < mindist) {
besti = i;
mindist = d;
}
}
return besti;
}
/*
* Allocate a color in the X color map and return the index.
*/
static int
alloc_col(Display *dpy, Colormap cm, int r, int g, int b)
{
XColor col;
r = CLIP(r, 0, 255);
g = CLIP(g, 0, 255);
b = CLIP(b, 0, 255);
col.flags = DoRed | DoGreen | DoBlue;
col.red = (r << 8) | r;
col.green = (g << 8) | g;
col.blue = (b << 8) | b;
#ifdef NETSCAPE
if ((remaining_colors > 0) && XAllocColor(dpy, cm, &col)) {
remaining_colors--;
#else
if (XAllocColor(dpy, cm, &col)) {
#endif
#ifdef DEBUG
if (debug_colormap)
printf("allocated %d (%d,%d, %d)\n", col.pixel, r, g, b);
#endif
awt_Colors[col.pixel].flags = ALLOCATED_COLOR;
awt_Colors[col.pixel].r = col.red >> 8;
awt_Colors[col.pixel].g = col.green >> 8;
awt_Colors[col.pixel].b = col.blue >> 8;
return col.pixel;
}
return awt_color_match(r, g, b);
}
/*
* called from X11Server_create() in xlib.c
*/
int
awt_allocate_colors()
{
Display *dpy;
unsigned long freecolors[256], plane_masks[1];
XColor cols[256];
Colormap cm;
int i, ri, gi, bi, nfree, depth, screen;
XPixmapFormatValues *pPFV;
int numpfv;
XVisualInfo *pVI;
char *forcemono;
char *forcegray;
forcemono = getenv("FORCEMONO");
forcegray = getenv("FORCEGRAY");
if (forcemono && !forcegray)
forcegray = forcemono;
/*
* Get the colormap and make sure we have the right visual
*/
dpy = awt_display;
screen = awt_screen;
cm = awt_cmap;
depth = awt_depth;
pVI = &awt_visInfo;
awt_num_colors = awt_visInfo.colormap_size;
if (depth > 8) {
int size;
awtImage = &awtImageTC;
awtImage->Depth = depth;
awtImage->rOff = 0;
for (i = pVI->red_mask; (i & 1) == 0; i >>= 1) {
awtImage->rOff++;
}
size = 0;
for (; i; i >>= 1) {
size++;
}
awtImage->rChop = 8-size;
awtImage->gOff = 0;
for (i = pVI->green_mask; (i & 1) == 0; i >>= 1) {
awtImage->gOff++;
}
size = 0;
for (; i; i >>= 1) {
size++;
}
awtImage->gChop = 8-size;
awtImage->bOff = 0;
for (i = pVI->blue_mask; (i & 1) == 0; i >>= 1) {
awtImage->bOff++;
}
size = 0;
for (; i; i >>= 1) {
size++;
}
awtImage->bChop = 8-size;
} else if (pVI->class == StaticGray || pVI->class == GrayScale
|| (forcegray && depth <= 8)) {
awtImage = &awtImageGS;
awtImage->Depth = depth;
} else {
awtImage = &awtImage8;
awtImage->Depth = depth;
#ifdef NETSCAPE
remaining_colors = (*awt_GetMaxColors)();
#endif
}
pPFV = XListPixmapFormats(dpy, &numpfv);
if (pPFV) {
for (i = 0; i < numpfv; i++) {
if (pPFV[i].depth == depth) {
awtImage->wsImageFormat = pPFV[i];
break;
}
}
XFree(pPFV);
}
if (depth > 8) {
return 1;
}
if (awt_num_colors > 256) {
return 0;
}
/*
* Initialize colors array
*/
for (i = 0; i < awt_num_colors; i++) {
cols[i].pixel = i;
}
XQueryColors(dpy, cm, cols, awt_num_colors);
for (i = 0; i < awt_num_colors; i++) {
awt_Colors[i].r = cols[i].red >> 8;
awt_Colors[i].g = cols[i].green >> 8;
awt_Colors[i].b = cols[i].blue >> 8;
awt_Colors[i].flags = LIKELY_COLOR;
}
/*
* Determine which colors in the colormap can be allocated and mark
* them in the colors array
*/
nfree = 0;
for (i = 128; i > 0; i >>= 1) {
if (XAllocColorCells(dpy, cm, False, plane_masks, 0,
freecolors + nfree, i)) {
nfree += i;
}
}
for (i = 0; i < nfree; i++) {
awt_Colors[freecolors[i]].flags = FREE_COLOR;
}
#ifdef DEBUG
if (debug_colormap) {
printf("%d free.\n", nfree);
}
#endif
XFreeColors(dpy, cm, freecolors, nfree, 0);
/*
* Allocate the colors that are already allocated by other
* applications
*/
for (i = 0; i < awt_num_colors; i++) {
if (awt_Colors[i].flags == LIKELY_COLOR) {
awt_Colors[i].flags = FREE_COLOR;
alloc_col(dpy, cm,
awt_Colors[i].r,
awt_Colors[i].g,
awt_Colors[i].b);
}
}
#ifdef DEBUG
if (debug_colormap) {
printf("got the already allocated ones\n");
}
#endif
/*
* Allocate more colors, filling the color space evenly.
*/
alloc_col(dpy, cm, 255, 255, 255);
alloc_col(dpy, cm, 255, 0, 0);
if (awtImage == &awtImageGS) {
int g;
if (!forcemono) {
for (i = 128; i > 0; i >>= 1) {
for (g = i; g < 256; g += (i * 2)) {
alloc_col(dpy, cm, g, g, g);
}
}
}
for (g = 0; g < 256; g++) {
ColorEntry *p;
int mindist, besti;
int d;
p = awt_Colors;
mindist = 256;
besti = 0;
for (i = 0 ; i < awt_num_colors ; i++, p++) {
if (forcegray && (p->r != p->g || p->g != p->b))
continue;
if (forcemono && p->g != 0 && p->g != 255)
continue;
if (p->flags == ALLOCATED_COLOR) {
d = p->g - g;
if (d < 0) d = -d;
if (d < mindist) {
besti = i;
if (d == 0) {
break;
}
mindist = d;
}
}
}
grays[g] = besti;
}
if (forcemono || (depth == 1)) {
double pow(double, double);
char *gammastr = getenv("HJGAMMA");
double gamma = atof(gammastr ? gammastr : "1.6");
if (gamma < 0.01) gamma = 1.0;
#ifdef DEBUG
if (debug_colormap) {
fprintf(stderr, "gamma = %f\n", gamma);
}
#endif
for (i = 0; i < 256; i++) {
bwgamma[i] = (int) (pow(i/255.0, gamma) * 255);
#ifdef DEBUG
if (debug_colormap) {
fprintf(stderr, "%3d ", bwgamma[i]);
if ((i & 7) == 7)
fprintf(stderr, "\n");
}
#endif
}
} else {
for (i = 0; i < 256; i++) {
bwgamma[i] = i;
}
}
/* Now we fill in the color cube so that the RGB dithering
* algorithm can dither the grayscale values.
*/
for (bi = 0 ; bi <= CMAP_COLS ; bi += 1) {
int b = bi << (8 - CMAP_BITS);
for (gi = 0 ; gi <= CMAP_COLS ; gi += 1) {
int g = gi << (8 - CMAP_BITS);
for (ri = 0 ; ri <= CMAP_COLS ; ri += 1) {
int r = ri << (8 - CMAP_BITS);
if (awt_RGBCube[ri][gi][bi] == 0) {
awt_RGBCube[ri][gi][bi] = grays[RGBTOGRAY(r, g, b)];
}
}
}
}
#ifdef DEBUG
if (debug_colormap) {
fprintf(stderr, "GrayScale initialized\n");
fprintf(stderr, "color table:\n");
for (i = 0; i < awt_num_colors; i++) {
fprintf(stderr, "%3d: %3d %3d %3d\n",
i, awt_Colors[i].r, awt_Colors[i].g, awt_Colors[i].b);
}
fprintf(stderr, "gray table:\n");
for (i = 0; i < 256; i++) {
fprintf(stderr, "%3d ", grays[i]);
if ((i & 7) == 7)
fprintf(stderr, "\n");
}
}
#endif
return 1;
}
alloc_col(dpy, cm, 0, 255, 0);
alloc_col(dpy, cm, 0, 0, 255);
alloc_col(dpy, cm, 255, 255, 0);
alloc_col(dpy, cm, 255, 0, 255);
alloc_col(dpy, cm, 0, 255, 255);
alloc_col(dpy, cm, 235, 235, 235);
alloc_col(dpy, cm, 224, 224, 224);
alloc_col(dpy, cm, 214, 214, 214);
alloc_col(dpy, cm, 192, 192, 192);
alloc_col(dpy, cm, 162, 162, 162);
alloc_col(dpy, cm, 128, 128, 128);
alloc_col(dpy, cm, 105, 105, 105);
alloc_col(dpy, cm, 64, 64, 64);
alloc_col(dpy, cm, 32, 32, 32);
alloc_col(dpy, cm, 255, 128, 128);
alloc_col(dpy, cm, 128, 255, 128);
alloc_col(dpy, cm, 128, 128, 255);
alloc_col(dpy, cm, 255, 255, 128);
alloc_col(dpy, cm, 255, 128, 255);
alloc_col(dpy, cm, 128, 255, 255);
#ifdef DEBUG
if (debug_colormap)
printf("got critical ones\n");
#endif
/* allocate some colors */
for (bi = 0 ; bi <= CMAP_COLS ; bi += 4) {
int b = bi << (8 - CMAP_BITS);
for (gi = 0 ; gi <= CMAP_COLS ; gi += 2) {
int g = gi << (8 - CMAP_BITS);
for (ri = 0; ri <= CMAP_COLS ; ri += 1) {
int r = ri << (8 - CMAP_BITS);
if (awt_RGBCube[ri][gi][bi] == 0) {
awt_RGBCube[ri][gi][bi] = alloc_col(dpy, cm, r, g, b);
}
}
}
}
/* match remainder */
for (bi = 0 ; bi <= CMAP_COLS ; bi += 1) {
int b = bi << (8 - CMAP_BITS);
for (gi = 0 ; gi <= CMAP_COLS ; gi += 1) {
int g = gi << (8 - CMAP_BITS);
for (ri = 0 ; ri <= CMAP_COLS ; ri += 1) {
int r = ri << (8 - CMAP_BITS);
if (awt_RGBCube[ri][gi][bi] == 0) {
awt_RGBCube[ri][gi][bi] = awt_color_match(r, g, b);
}
}
}
}
#ifdef DEBUG
if (debug_colormap) {
printf("got cube\n");
}
#endif
#ifdef DEBUG
if (debug_colormap) {
int alloc_count = 0;
int reuse_count = 0;
int free_count = 0;
int saw[256];
for (i = 0; i < awt_num_colors; i++) {
switch (awt_Colors[i].flags) {
case ALLOCATED_COLOR:
alloc_count++;
break;
case LIKELY_COLOR:
reuse_count++;
break;
case FREE_COLOR:
free_count++;
break;
}
}
printf("%d total, %d allocated, %d reused, %d still free.\n",
awt_num_colors, alloc_count, reuse_count, free_count);
for (i = 0; i < awt_num_colors; i++) {
saw[i] = 0;
}
for (bi = 0; bi <= CMAP_COLS; bi++) {
for (gi = 0; gi <= CMAP_COLS; gi++) {
for (ri = 0; ri <= CMAP_COLS; ri++) {
saw[awt_RGBCube[ri][gi][bi]]++;
}
}
}
for (i = 0; i < awt_num_colors; i++) {
printf("%d %d\n", i, saw[i]);
}
}
#endif
return 1;
}
struct Hjava_awt_image_ColorModel *
awt_getColorModel() {
struct Hjava_awt_image_ColorModel *awt_colormodel;
if (awt_visInfo.class == TrueColor) {
awt_colormodel = (struct Hjava_awt_image_ColorModel *)
execute_java_constructor(EE(),
"java/awt/image/DirectColorModel",
0, "(IIIII)",
awt_visInfo.depth,
awt_visInfo.red_mask,
awt_visInfo.green_mask,
awt_visInfo.blue_mask,
0);
} else {
HArrayOfByte *hR = (HArrayOfByte *) ArrayAlloc(T_BYTE, 256);
HArrayOfByte *hG = (HArrayOfByte *) ArrayAlloc(T_BYTE, 256);
HArrayOfByte *hB = (HArrayOfByte *) ArrayAlloc(T_BYTE, 256);
char *aBase, *a;
ColorEntry *c;
/* These loops copy backwards so that the aBase pointer won't
* be dropped by the optimizer and allow the garbage collector
* to move the array being initialized.
*/
aBase = unhand(hR)->body;
for (a = aBase + 255, c = awt_Colors + 255; a >= aBase; a--, c--) {
*a = c->r;
}
aBase = unhand(hG)->body;
for (a = aBase + 255, c = awt_Colors + 255; a >= aBase; a--, c--) {
*a = c->g;
}
aBase = unhand(hB)->body;
for (a = aBase + 255, c = awt_Colors + 255; a >= aBase; a--, c--) {
*a = c->b;
}
awt_colormodel = (struct Hjava_awt_image_ColorModel *)
execute_java_constructor(EE(),
"java/awt/image/IndexColorModel",
0, "(II[B[B[B)",
awt_visInfo.depth,
awt_num_colors,
hR, hG, hB);
}
return awt_colormodel;
}
#define red(v) (((v) >> 16) & 0xFF)
#define green(v) (((v) >> 8) & 0xFF)
#define blue(v) (((v) >> 0) & 0xFF)
int
awt_getColor(struct Hjava_awt_Color *this)
{
if (this) {
int col = unhand(this)->pData;
if (col) {
return col - 1;
}
col = unhand(this)->value;
col = awtImage->ColorMatch(red(col), green(col), blue(col));
unhand(this)->pData = col + 1;
return col;
}
return 0;
}