/*
 * Copyright (c) 1995, 2004, 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.
 */

/*
 *      Image dithering and rendering code for X11.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <alloca.h>
#ifndef HEADLESS
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#endif /* !HEADLESS */
#include "awt_p.h"
#include "java_awt_Color.h"
#include "java_awt_SystemColor.h"
#include "java_awt_color_ColorSpace.h"
#include "java_awt_Transparency.h"
#include "java_awt_image_DataBuffer.h"
#include "img_colors.h"
#include "imageInitIDs.h"
#include "dither.h"

#include <jni.h>
#include <jni_util.h>

#ifdef DEBUG
static int debug_colormap = 0;
#endif

#define MAX_PALETTE8_SIZE (256)
#define MAX_PALETTE12_SIZE (4096)
#define MAX_PALETTE_SIZE MAX_PALETTE12_SIZE

/* returns the absolute value x */
#define ABS(x) ((x) < 0 ? -(x) : (x))

#define CLIP(val,min,max)       ((val < min) ? min : ((val > max) ? max : val))

#define RGBTOGRAY(r, g, b) ((int) (.299 * r + .587 * g + .114 * b + 0.5))

enum {
    FREE_COLOR          = 0,
    LIKELY_COLOR        = 1,
    UNAVAILABLE_COLOR   = 2,
    ALLOCATED_COLOR     = 3
};

/*
 * Constants to control the filling of the colormap.
 * By default, try to allocate colors in the default colormap until
 * CMAP_ALLOC_DEFAULT colors are being used (by Java and/or other
 * applications).
 * For cases where the default colormap may already have a large
 * number of colors in it, make sure that we ourselves try to add
 * at least CMAP_ALLOC_MIN new colors, even if we need to allocate
 * more than the DEFAULT to do that.
 * Under no circumstances will the colormap be filled to more than
 * CMAP_ALLOC_MAX colors.
 */
#define CMAP_ALLOC_MIN          100     /* minimum number of colors to "add" */
#define CMAP_ALLOC_DEFAULT      200     /* default number of colors in cmap */
#define CMAP_ALLOC_MAX          245     /* maximum number of colors in cmap */

#ifdef __solaris__
#include <sys/utsname.h>

struct {
    char *machine;
    int  cubesize;
} machinemap[] = {
    { "i86pc", LOOKUPSIZE / 4 }, /* BugTraq ID 4102599 */
    { "sun4c", LOOKUPSIZE / 4 },
    { "sun4m", LOOKUPSIZE / 2 },
    { "sun4d", LOOKUPSIZE / 2 },
    { "sun4u", LOOKUPSIZE / 1 },
};

#define MACHMAPSIZE     (sizeof(machinemap) / sizeof(machinemap[0]))

int getVirtCubeSize() {
    struct utsname name;
    int i, ret;

    ret = uname(&name);
    if (ret < 0) {
#ifdef DEBUG
#include <errno.h>
        jio_fprintf(stderr, "uname errno = %d, using default cubesize %d\n",
                    errno, LOOKUPSIZE);
#endif
        return LOOKUPSIZE;
    }

    for (i = 0; i < MACHMAPSIZE; i++) {
        if (strcmp(name.machine, machinemap[i].machine) == 0) {
#ifdef DEBUG
            if (debug_colormap) {
                jio_fprintf(stderr, "'%s'.cubesize = '%d'\n",
                            machinemap[i].machine, machinemap[i].cubesize);
            }
#endif
            return machinemap[i].cubesize;
        }
    }

#ifdef DEBUG
    if (debug_colormap) {
        jio_fprintf(stderr, "unknown machine '%s' using cubesize %d\n",
                    name.machine, LOOKUPSIZE);
    }
#endif
    return LOOKUPSIZE;
}
#else /* __solaris__ */
#define getVirtCubeSize()       (LOOKUPSIZE)
#endif /* __solaris__ */

unsigned char img_bwgamma[256];
uns_ordered_dither_array img_oda_alpha;

#ifdef NEED_IMAGE_CONVERT
ImgConvertFcn DirectImageConvert;
ImgConvertFcn Dir16IcmOpqUnsImageConvert;
ImgConvertFcn Dir16IcmTrnUnsImageConvert;
ImgConvertFcn Dir16IcmOpqSclImageConvert;
ImgConvertFcn Dir16DcmOpqUnsImageConvert;
ImgConvertFcn Dir16DcmTrnUnsImageConvert;
ImgConvertFcn Dir16DcmOpqSclImageConvert;
ImgConvertFcn Dir32IcmOpqUnsImageConvert;
ImgConvertFcn Dir32IcmTrnUnsImageConvert;
ImgConvertFcn Dir32IcmOpqSclImageConvert;
ImgConvertFcn Dir32DcmOpqUnsImageConvert;
ImgConvertFcn Dir32DcmTrnUnsImageConvert;
ImgConvertFcn Dir32DcmOpqSclImageConvert;

ImgConvertFcn PseudoImageConvert;
ImgConvertFcn PseudoFSImageConvert;
ImgConvertFcn FSColorIcmOpqUnsImageConvert;
ImgConvertFcn FSColorDcmOpqUnsImageConvert;
ImgConvertFcn OrdColorIcmOpqUnsImageConvert;
ImgConvertFcn OrdColorDcmOpqUnsImageConvert;

#endif /* NEED_IMAGE_CONVERT */

#ifndef HEADLESS
/*
 * Find the best color.
 */
int
awt_color_matchTC(int r, int g, int b, AwtGraphicsConfigDataPtr awt_data)
{
    r = CLIP(r, 0, 255);
    g = CLIP(g, 0, 255);
    b = CLIP(b, 0, 255);
    return (((r >> awt_data->awtImage->clrdata.rScale)
                << awt_data->awtImage->clrdata.rOff) |
            ((g >> awt_data->awtImage->clrdata.gScale)
                << awt_data->awtImage->clrdata.gOff) |
            ((b >> awt_data->awtImage->clrdata.bScale)
                << awt_data->awtImage->clrdata.bOff));
}

int
awt_color_matchGS(int r, int g, int b, AwtGraphicsConfigDataPtr awt_data)
{
    r = CLIP(r, 0, 255);
    g = CLIP(g, 0, 255);
    b = CLIP(b, 0, 255);
    return awt_data->color_data->img_grays[RGBTOGRAY(r, g, b)];
}

int
awt_color_match(int r, int g, int b, AwtGraphicsConfigDataPtr awt_data)
{
    int besti = 0;
    int mindist, i, t, d;
    ColorEntry *p = awt_data->color_data->awt_Colors;

    r = CLIP(r, 0, 255);
    g = CLIP(g, 0, 255);
    b = CLIP(b, 0, 255);

    /* look for pure gray match */
    if ((r == g) && (g == b)) {
      mindist = 256;
      for (i = 0 ; i < awt_data->awt_num_colors ; i++, p++)
        if (p->flags == ALLOCATED_COLOR) {
          if (! ((p->r == p->g) && (p->g == p->b)) )
              continue;
          d = ABS(p->r - r);
          if (d == 0)
              return i;
          if (d < mindist) {
              besti = i;
              mindist = d;
          }
        }
      return besti;
    }

    /* look for non-pure gray match */
    mindist = 256 * 256 * 256;
    for (i = 0 ; i < awt_data->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 pixel.
 * If the "expected pixel" is non-negative then we will only
 * accept the allocation if we get exactly that pixel value.
 * This prevents us from seeing a bunch of ReadWrite pixels
 * allocated by another imaging application and duplicating
 * that set of inaccessible pixels in our precious remaining
 * ReadOnly colormap cells.
 */
static int
alloc_col(Display *dpy, Colormap cm, int r, int g, int b, int pixel,
          AwtGraphicsConfigDataPtr awt_data)
{
    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;
    if (XAllocColor(dpy, cm, &col)) {
#ifdef DEBUG
        if (debug_colormap)
            jio_fprintf(stdout, "allocated %d (%d,%d, %d)\n", col.pixel, r, g, b);
#endif
        if (pixel >= 0 && col.pixel != (unsigned long)pixel) {
            /*
             * If we were trying to allocate a shareable "ReadOnly"
             * color then we would have gotten back the expected
             * pixel.  If the returned pixel was different, then
             * the source color that we were attempting to gain
             * access to must be some other application's ReadWrite
             * private color.  We free the returned pixel so that
             * we won't waste precious colormap entries by duplicating
             * that color in the as yet unallocated entries.  We
             * return -1 here to indicate the failure to get the
             * expected pixel.
             */
#ifdef DEBUG
            if (debug_colormap)
                jio_fprintf(stdout, "   used by other app, freeing\n");
#endif
            awt_data->color_data->awt_Colors[pixel].flags = UNAVAILABLE_COLOR;
            XFreeColors(dpy, cm, &col.pixel, 1, 0);
            return -1;
        }
        /*
         * Our current implementation doesn't support pixels which
         * don't fit in 8 bit (even for 12-bit visuals)
         */
        if (col.pixel > 255) {
#ifdef DEBUG
            if (debug_colormap)
                jio_fprintf(stdout, "pixel %d for (%d,%d, %d) is > 8 bit, releasing.\n",
                            col.pixel, r, g, b);
#endif
            XFreeColors(dpy, cm, &col.pixel, 1, 0);
            return awt_color_match(r, g, b, awt_data);
        }

        awt_data->color_data->awt_Colors[col.pixel].flags = ALLOCATED_COLOR;
        awt_data->color_data->awt_Colors[col.pixel].r = col.red   >> 8;
        awt_data->color_data->awt_Colors[col.pixel].g = col.green >> 8;
        awt_data->color_data->awt_Colors[col.pixel].b = col.blue  >> 8;
        if (awt_data->color_data->awt_icmLUT != 0) {
            awt_data->color_data->awt_icmLUT2Colors[col.pixel] = col.pixel;
            awt_data->color_data->awt_icmLUT[col.pixel] =
                0xff000000 |
                (awt_data->color_data->awt_Colors[col.pixel].r<<16) |
                (awt_data->color_data->awt_Colors[col.pixel].g<<8) |
                (awt_data->color_data->awt_Colors[col.pixel].b);
        }
        return col.pixel;
#ifdef DEBUG
    } else if (debug_colormap) {
        jio_fprintf(stdout, "can't allocate (%d,%d, %d)\n", r, g, b);
#endif
    }

    return awt_color_match(r, g, b, awt_data);
}

void
awt_allocate_systemcolors(XColor *colorsPtr, int num_pixels, AwtGraphicsConfigDataPtr awtData) {
    int i;
    int r, g, b, pixel;

    for (i=0; i < num_pixels; i++) {
        r = colorsPtr[i].red   >> 8;
        g = colorsPtr[i].green >> 8;
        b = colorsPtr[i].blue  >> 8;
        pixel = alloc_col(awt_display, awtData->awt_cmap, r, g, b, -1, awtData);
    }
}
#endif /* !HEADLESS */

void
awt_fill_imgcv(ImgConvertFcn **array, int mask, int value, ImgConvertFcn fcn)
{
    int i;

    for (i = 0; i < NUM_IMGCV; i++) {
        if ((i & mask) == value) {
            array[i] = fcn;
        }
    }
}

#ifndef HEADLESS
/*
 * called from X11Server_create() in xlib.c
 */
int
awt_allocate_colors(AwtGraphicsConfigDataPtr awt_data)
{
    Display *dpy;
    unsigned long freecolors[MAX_PALETTE_SIZE], plane_masks[1];
    int paletteSize;
    XColor cols[MAX_PALETTE_SIZE];
    unsigned char reds[256], greens[256], blues[256];
    int indices[256];
    Colormap cm;
    int i, j, k, cmapsize, nfree, depth, bpp;
    int allocatedColorsNum, unavailableColorsNum;
    XPixmapFormatValues *pPFV;
    int numpfv;
    XVisualInfo *pVI;
    char *forcemono;
    char *forcegray;

    make_uns_ordered_dither_array(img_oda_alpha, 256);


    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;
    cm = awt_data->awt_cmap;
    depth = awt_data->awt_depth;
    pVI = &awt_data->awt_visInfo;
    awt_data->awt_num_colors = awt_data->awt_visInfo.colormap_size;
    awt_data->awtImage = (awtImageData *) calloc (1, sizeof (awtImageData));

    pPFV = XListPixmapFormats(dpy, &numpfv);
    if (pPFV) {
        for (i = 0; i < numpfv; i++) {
            if (pPFV[i].depth == depth) {
                awt_data->awtImage->wsImageFormat = pPFV[i];
                break;
            }
        }
        XFree(pPFV);
    }
    bpp = awt_data->awtImage->wsImageFormat.bits_per_pixel;
    if (bpp == 24) {
        bpp = 32;
    }
    awt_data->awtImage->clrdata.bitsperpixel = bpp;
    awt_data->awtImage->Depth = depth;

    if ((bpp == 32 || bpp == 16) && pVI->class == TrueColor && depth >= 15) {
        awt_data->AwtColorMatch = awt_color_matchTC;
        awt_data->awtImage->clrdata.rOff = 0;
        for (i = pVI->red_mask; (i & 1) == 0; i >>= 1) {
            awt_data->awtImage->clrdata.rOff++;
        }
        awt_data->awtImage->clrdata.rScale = 0;
        while (i < 0x80) {
            awt_data->awtImage->clrdata.rScale++;
            i <<= 1;
        }
        awt_data->awtImage->clrdata.gOff = 0;
        for (i = pVI->green_mask; (i & 1) == 0; i >>= 1) {
            awt_data->awtImage->clrdata.gOff++;
        }
        awt_data->awtImage->clrdata.gScale = 0;
        while (i < 0x80) {
            awt_data->awtImage->clrdata.gScale++;
            i <<= 1;
        }
        awt_data->awtImage->clrdata.bOff = 0;
        for (i = pVI->blue_mask; (i & 1) == 0; i >>= 1) {
            awt_data->awtImage->clrdata.bOff++;
        }
        awt_data->awtImage->clrdata.bScale = 0;
        while (i < 0x80) {
            awt_data->awtImage->clrdata.bScale++;
            i <<= 1;
        }
#ifdef NEED_IMAGE_CONVERT
        awt_fill_imgcv(awt_data->awtImage->convert, 0, 0, DirectImageConvert);
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_BYTEIN
                        | IMGCV_OPAQUE | IMGCV_ICM),
                       (bpp == 32
                        ? Dir32IcmOpqUnsImageConvert
                        : Dir16IcmOpqUnsImageConvert));
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_BYTEIN
                        | IMGCV_ALPHA | IMGCV_ICM),
                       (bpp == 32
                        ? Dir32IcmTrnUnsImageConvert
                        : Dir16IcmTrnUnsImageConvert));
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_SCALED | IMGCV_BYTEIN
                        | IMGCV_OPAQUE | IMGCV_ICM),
                       (bpp == 32
                        ? Dir32IcmOpqSclImageConvert
                        : Dir16IcmOpqSclImageConvert));
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_INTIN
                        | IMGCV_OPAQUE | IMGCV_DCM8),
                       (bpp == 32
                        ? Dir32DcmOpqUnsImageConvert
                        : Dir16DcmOpqUnsImageConvert));
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_INTIN
                        | IMGCV_ALPHA | IMGCV_DCM8),
                       (bpp == 32
                        ? Dir32DcmTrnUnsImageConvert
                        : Dir16DcmTrnUnsImageConvert));
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                        | IMGCV_ALPHABITS | IMGCV_CMBITS),
                       (IMGCV_SCALED | IMGCV_INTIN
                        | IMGCV_OPAQUE | IMGCV_DCM8),
                       (bpp == 32
                        ? Dir32DcmOpqSclImageConvert
                        : Dir16DcmOpqSclImageConvert));
#endif /* NEED_IMAGE_CONVERT */
    } else if (bpp <= 16 && (pVI->class == StaticGray
                            || pVI->class == GrayScale
                            || (pVI->class == PseudoColor && forcegray))) {
        awt_data->AwtColorMatch = awt_color_matchGS;
        awt_data->awtImage->clrdata.grayscale = 1;
        awt_data->awtImage->clrdata.bitsperpixel = MAX(bpp, 8);
#ifdef NEED_IMAGE_CONVERT
        awt_fill_imgcv(awt_data->awtImage->convert, 0, 0, PseudoImageConvert);
        if (getenv("NOFSDITHER") == NULL) {
            awt_fill_imgcv(awt_data->awtImage->convert,
                           IMGCV_ORDERBITS, IMGCV_TDLRORDER,
                           PseudoFSImageConvert);
        }
#endif /* NEED_IMAGE_CONVERT */
    } else if (depth <= 12 && (pVI->class == PseudoColor
                             || pVI->class == TrueColor
                             || pVI->class == StaticColor)) {
        if (pVI->class == TrueColor)
           awt_data->awt_num_colors = (1 << pVI->depth);
        awt_data->AwtColorMatch = awt_color_match;
        awt_data->awtImage->clrdata.bitsperpixel = MAX(bpp, 8);
#ifdef NEED_IMAGE_CONVERT
        awt_fill_imgcv(awt_data->awtImage->convert, 0, 0, PseudoImageConvert);
        if (getenv("NOFSDITHER") == NULL) {
            awt_fill_imgcv(awt_data->awtImage->convert, IMGCV_ORDERBITS,
                           IMGCV_TDLRORDER, PseudoFSImageConvert);
            awt_fill_imgcv(awt_data->awtImage->convert,
                           (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                            | IMGCV_ALPHABITS | IMGCV_ORDERBITS
                            | IMGCV_CMBITS),
                           (IMGCV_UNSCALED | IMGCV_BYTEIN
                            | IMGCV_OPAQUE | IMGCV_TDLRORDER
                            | IMGCV_ICM),
                           FSColorIcmOpqUnsImageConvert);
            awt_fill_imgcv(awt_data->awtImage->convert,
                           (IMGCV_SCALEBITS | IMGCV_INSIZEBITS
                            | IMGCV_ALPHABITS | IMGCV_ORDERBITS
                            | IMGCV_CMBITS),
                           (IMGCV_UNSCALED | IMGCV_INTIN
                            | IMGCV_OPAQUE | IMGCV_TDLRORDER
                            | IMGCV_DCM8),
                           FSColorDcmOpqUnsImageConvert);
        }
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS | IMGCV_ALPHABITS
                        | IMGCV_ORDERBITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_BYTEIN | IMGCV_OPAQUE
                        | IMGCV_RANDORDER | IMGCV_ICM),
                       OrdColorIcmOpqUnsImageConvert);
        awt_fill_imgcv(awt_data->awtImage->convert,
                       (IMGCV_SCALEBITS | IMGCV_INSIZEBITS | IMGCV_ALPHABITS
                        | IMGCV_ORDERBITS | IMGCV_CMBITS),
                       (IMGCV_UNSCALED | IMGCV_INTIN | IMGCV_OPAQUE
                        | IMGCV_RANDORDER | IMGCV_DCM8),
                       OrdColorDcmOpqUnsImageConvert);
#endif /* NEED_IMAGE_CONVERT */
    } else {
        free (awt_data->awtImage);
        return 0;
    }

    if (depth > 12) {
        return 1;
    }

    if (depth == 12) {
        paletteSize = MAX_PALETTE12_SIZE;
    } else {
        paletteSize = MAX_PALETTE8_SIZE;
    }

    if (awt_data->awt_num_colors > paletteSize) {
        free (awt_data->awtImage);
        return 0;
    }

    /* Allocate ColorData structure */
    awt_data->color_data = ZALLOC (_ColorData);
    awt_data->color_data->screendata = 1; /* This ColorData struct corresponds
                                             to some AWT screen/visual, so when
                                             any IndexColorModel using this
                                             struct is finalized, don't free
                                             the struct in freeICMColorData.
                                           */

    /*
     * Initialize colors array
     */
    for (i = 0; i < awt_data->awt_num_colors; i++) {
        cols[i].pixel = i;
    }

    awt_data->color_data->awt_Colors =
        (ColorEntry *)calloc(paletteSize, sizeof (ColorEntry));

    XQueryColors(dpy, cm, cols, awt_data->awt_num_colors);
    for (i = 0; i < awt_data->awt_num_colors; i++) {
        awt_data->color_data->awt_Colors[i].r = cols[i].red >> 8;
        awt_data->color_data->awt_Colors[i].g = cols[i].green >> 8;
        awt_data->color_data->awt_Colors[i].b = cols[i].blue >> 8;
        awt_data->color_data->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 = (paletteSize / 2); i > 0; i >>= 1) {
        if (XAllocColorCells(dpy, cm, False, plane_masks, 0,
                             freecolors + nfree, i)) {
            nfree += i;
        }
    }

    for (i = 0; i < nfree; i++) {
        awt_data->color_data->awt_Colors[freecolors[i]].flags = FREE_COLOR;
    }

#ifdef DEBUG
    if (debug_colormap) {
        jio_fprintf(stdout, "%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_data->awt_num_colors; i++) {
        if (awt_data->color_data->awt_Colors[i].flags == LIKELY_COLOR) {
            awt_data->color_data->awt_Colors[i].flags = FREE_COLOR;
            alloc_col(dpy, cm,
                      awt_data->color_data->awt_Colors[i].r,
                      awt_data->color_data->awt_Colors[i].g,
                      awt_data->color_data->awt_Colors[i].b, i, awt_data);
        }
    }
#ifdef DEBUG
    if (debug_colormap) {
        jio_fprintf(stdout, "got the already allocated ones\n");
    }
#endif

    /*
     * Allocate more colors, filling the color space evenly.
     */

    alloc_col(dpy, cm, 255, 255, 255, -1, awt_data);
    alloc_col(dpy, cm, 0, 0, 0, -1, awt_data);

    if (awt_data->awtImage->clrdata.grayscale) {
        int g;
        ColorEntry *p;

        if (!forcemono) {
            for (i = 128; i > 0; i >>= 1) {
                for (g = i; g < 256; g += (i * 2)) {
                    alloc_col(dpy, cm, g, g, g, -1, awt_data);
                }
            }
        }

        awt_data->color_data->img_grays =
            (unsigned char *)calloc(256, sizeof(unsigned char));
        for (g = 0; g < 256; g++) {
            int mindist, besti;
            int d;

            p = awt_data->color_data->awt_Colors;
            mindist = 256;
            besti = 0;
            for (i = 0 ; i < awt_data->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;
                    }
                }
            }

            awt_data->color_data->img_grays[g] = besti;
        }


        if (forcemono || (depth == 1)) {
            char *gammastr = getenv("HJGAMMA");
            double gamma = atof(gammastr ? gammastr : "1.6");
            if (gamma < 0.01) gamma = 1.0;
#ifdef DEBUG
            if (debug_colormap) {
                jio_fprintf(stderr, "gamma = %f\n", gamma);
            }
#endif
            for (i = 0; i < 256; i++) {
                img_bwgamma[i] = (int) (pow(i/255.0, gamma) * 255);
#ifdef DEBUG
                if (debug_colormap) {
                    jio_fprintf(stderr, "%3d ", img_bwgamma[i]);
                    if ((i & 7) == 7)
                        jio_fprintf(stderr, "\n");
                }
#endif
            }
        } else {
            for (i = 0; i < 256; i++) {
                img_bwgamma[i] = i;
            }
        }

#ifdef DEBUG
        if (debug_colormap) {
            jio_fprintf(stderr, "GrayScale initialized\n");
            jio_fprintf(stderr, "color table:\n");
            for (i = 0; i < awt_data->awt_num_colors; i++) {
                jio_fprintf(stderr, "%3d: %3d %3d %3d\n",
                        i, awt_data->color_data->awt_Colors[i].r,
                        awt_data->color_data->awt_Colors[i].g,
                        awt_data->color_data->awt_Colors[i].b);
            }
            jio_fprintf(stderr, "gray table:\n");
            for (i = 0; i < 256; i++) {
                jio_fprintf(stderr, "%3d ", awt_data->color_data->img_grays[i]);
                if ((i & 7) == 7)
                    jio_fprintf(stderr, "\n");
            }
        }
#endif

    } else {

        alloc_col(dpy, cm, 255, 0, 0, -1, awt_data);
        alloc_col(dpy, cm, 0, 255, 0, -1,awt_data);
        alloc_col(dpy, cm, 0, 0, 255, -1,awt_data);
        alloc_col(dpy, cm, 255, 255, 0, -1,awt_data);
        alloc_col(dpy, cm, 255, 0, 255, -1,awt_data);
        alloc_col(dpy, cm, 0, 255, 255, -1,awt_data);
        alloc_col(dpy, cm, 192, 192, 192, -1,awt_data);
        alloc_col(dpy, cm, 255, 128, 128, -1,awt_data);
        alloc_col(dpy, cm, 128, 255, 128, -1,awt_data);
        alloc_col(dpy, cm, 128, 128, 255, -1,awt_data);
        alloc_col(dpy, cm, 255, 255, 128, -1,awt_data);
        alloc_col(dpy, cm, 255, 128, 255, -1,awt_data);
        alloc_col(dpy, cm, 128, 255, 255, -1,awt_data);
    }

    allocatedColorsNum = 0;
    unavailableColorsNum = 0;
    /* we do not support more than 256 entries in the colormap
       even for 12-bit PseudoColor visuals */
    for (i = 0; i < MAX_PALETTE8_SIZE; i++) {
        if (awt_data->color_data->awt_Colors[i].flags == ALLOCATED_COLOR)
        {
            reds[allocatedColorsNum] = awt_data->color_data->awt_Colors[i].r;
            greens[allocatedColorsNum] = awt_data->color_data->awt_Colors[i].g;
            blues[allocatedColorsNum] = awt_data->color_data->awt_Colors[i].b;
            allocatedColorsNum++;
        } else if (awt_data->color_data->awt_Colors[i].flags ==
                                                        UNAVAILABLE_COLOR) {
            unavailableColorsNum++;
        }
    }

    if (depth > 8) {
        cmapsize = MAX_PALETTE8_SIZE - unavailableColorsNum;
    } else {
        cmapsize = 0;
        if (getenv("CMAPSIZE") != 0) {
            cmapsize = atoi(getenv("CMAPSIZE"));
        }

        if (cmapsize <= 0) {
            cmapsize = CMAP_ALLOC_DEFAULT;
        }

        if (cmapsize < allocatedColorsNum + unavailableColorsNum + CMAP_ALLOC_MIN) {
            cmapsize = allocatedColorsNum + unavailableColorsNum + CMAP_ALLOC_MIN;
        }

        if (cmapsize > CMAP_ALLOC_MAX) {
            cmapsize = CMAP_ALLOC_MAX;
        }

        if (cmapsize < allocatedColorsNum) {
            cmapsize = allocatedColorsNum;
        }
        cmapsize -= unavailableColorsNum;
    }

    k = 0;
    if (getenv("VIRTCUBESIZE") != 0) {
        k = atoi(getenv("VIRTCUBESIZE"));
    }
    if (k == 0 || (k & (k - 1)) != 0 || k > 32) {
        k = getVirtCubeSize();
    }
    awt_data->color_data->img_clr_tbl =
        (unsigned char *)calloc(LOOKUPSIZE * LOOKUPSIZE * LOOKUPSIZE,
                                sizeof(unsigned char));
    img_makePalette(cmapsize, k, LOOKUPSIZE, 50, 250,
                    allocatedColorsNum, TRUE, reds, greens, blues,
                    awt_data->color_data->img_clr_tbl);
                    /*img_clr_tbl);*/

    for (i = 0; i < cmapsize; i++) {
        indices[i] = alloc_col(dpy, cm, reds[i], greens[i], blues[i], -1,
                               awt_data);
    }
    for (i = 0; i < LOOKUPSIZE * LOOKUPSIZE * LOOKUPSIZE  ; i++) {
        awt_data->color_data->img_clr_tbl[i] =
            indices[awt_data->color_data->img_clr_tbl[i]];
    }

    awt_data->color_data->img_oda_red   = &(std_img_oda_red[0][0]);
    awt_data->color_data->img_oda_green = &(std_img_oda_green[0][0]);
    awt_data->color_data->img_oda_blue  = &(std_img_oda_blue[0][0]);
    make_dither_arrays(cmapsize, awt_data->color_data);
    std_odas_computed = 1;

#ifdef DEBUG
    if (debug_colormap) {
        int alloc_count = 0;
        int reuse_count = 0;
        int free_count = 0;
        for (i = 0; i < awt_data->awt_num_colors; i++) {
            switch (awt_data->color_data->awt_Colors[i].flags) {
              case ALLOCATED_COLOR:
                alloc_count++;
                break;
              case LIKELY_COLOR:
                reuse_count++;
                break;
              case FREE_COLOR:
                free_count++;
                break;
            }
        }
        jio_fprintf(stdout, "%d total, %d allocated, %d reused, %d still free.\n",
                    awt_data->awt_num_colors, alloc_count, reuse_count, free_count);
    }
#endif

    /* Fill in the ICM lut and lut2cmap mapping */
    awt_data->color_data->awt_numICMcolors = 0;
    awt_data->color_data->awt_icmLUT2Colors =
        (unsigned char *)calloc(paletteSize, sizeof (unsigned char));
    awt_data->color_data->awt_icmLUT = (int *)calloc(paletteSize, sizeof(int));
    for (i=0; i < paletteSize; i++) {
        /* Keep the mapping between this lut and the actual cmap */
        awt_data->color_data->awt_icmLUT2Colors
            [awt_data->color_data->awt_numICMcolors] = i;

        if (awt_data->color_data->awt_Colors[i].flags == ALLOCATED_COLOR) {
            /* Screen IndexColorModel LUTS are always xRGB */
            awt_data->color_data->awt_icmLUT
                    [awt_data->color_data->awt_numICMcolors++] = 0xff000000 |
                (awt_data->color_data->awt_Colors[i].r<<16) |
                (awt_data->color_data->awt_Colors[i].g<<8) |
                (awt_data->color_data->awt_Colors[i].b);
        } else {
            /* Screen IndexColorModel LUTS are always xRGB */
            awt_data->color_data->awt_icmLUT
                        [awt_data->color_data->awt_numICMcolors++] = 0;
        }
    }
    return 1;
}
#endif /* !HEADLESS */

#define red(v)          (((v) >> 16) & 0xFF)
#define green(v)        (((v) >>  8) & 0xFF)
#define blue(v)         (((v) >>  0) & 0xFF)

#ifndef HEADLESS
jobject awtJNI_GetColorModel(JNIEnv *env, AwtGraphicsConfigDataPtr aData)
{
    jobject awt_colormodel = NULL;
    jclass clazz;
    jmethodID mid;

    if ((*env)->PushLocalFrame(env, 16) < 0)
        return NULL;

    if ((aData->awt_visInfo.class == TrueColor) &&
        (aData->awt_depth >= 15))
    {
        clazz = (*env)->FindClass(env,"java/awt/image/DirectColorModel");

        mid = (*env)->GetMethodID(env,clazz,"<init>","(IIIII)V");

        if (mid == NULL) {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

        awt_colormodel = (*env)->NewObject(env,clazz, mid,
                                           aData->awt_visInfo.depth,
                                           aData->awt_visInfo.red_mask,
                                           aData->awt_visInfo.green_mask,
                                           aData->awt_visInfo.blue_mask,
                                           0);

        if(awt_colormodel == NULL)
        {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

    }
    else if (aData->awt_visInfo.class == StaticGray &&
             aData->awt_num_colors == 256) {
        jclass clazz1;
        jobject cspace = NULL;
        jint bits[1];
        jintArray bitsArray;
        jboolean falseboolean = JNI_FALSE;

        clazz1 = (*env)->FindClass(env,"java/awt/color/ColorSpace");
        mid = (*env)->GetStaticMethodID(env, clazz1, "getInstance",
              "(I)Ljava/awt/color/ColorSpace;");
        if (mid == NULL) {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }
        /* SECURITY: This is safe, because static methods cannot
         *           be overridden, and this method does not invoke
         *           client code
         */
        cspace = (*env)->CallStaticObjectMethod(env, clazz1, mid,
            java_awt_color_ColorSpace_CS_GRAY);
        if (cspace == NULL) {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

        bits[0] = 8;
        bitsArray = (*env)->NewIntArray(env, 1);
        if (bitsArray == NULL) {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        } else {
            (*env)->SetIntArrayRegion(env, bitsArray, 0, 1, bits);
        }

        clazz = (*env)->FindClass(env,"java/awt/image/ComponentColorModel");

        mid = (*env)->GetMethodID(env,clazz,"<init>",
            "(Ljava/awt/color/ColorSpace;[IZZII)V");

        if (mid == NULL) {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

        awt_colormodel = (*env)->NewObject(env,clazz, mid,
                                           cspace,
                                           bitsArray,
                                           falseboolean,
                                           falseboolean,
                                           java_awt_Transparency_OPAQUE,
                                           java_awt_image_DataBuffer_TYPE_BYTE);

        if(awt_colormodel == NULL)
        {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

    } else {
        jint rgb[MAX_PALETTE_SIZE];
        jbyte valid[MAX_PALETTE_SIZE / 8], *pValid;
        jintArray hArray;
        jobject validBits = NULL;
        ColorEntry *c;
        int i, allocAllGray, b, allvalid, paletteSize;
        jlong pData;

        if (aData->awt_visInfo.depth == 12) {
            paletteSize = MAX_PALETTE12_SIZE;
        } else {
            paletteSize = MAX_PALETTE8_SIZE;
        }

        c = aData->color_data->awt_Colors;
        pValid = &valid[sizeof(valid)];
        allocAllGray = 1;
        b = 0;
        allvalid = 1;

        for (i = 0; i < paletteSize; i++, c++) {
            if (c->flags == ALLOCATED_COLOR) {
                rgb[i] = (0xff000000 |
                          (c->r << 16) |
                          (c->g <<  8) |
                          (c->b <<  0));
                if (c->r != c->g || c->g != c->b) {
                    allocAllGray = 0;
                }
                b |= (1 << (i % 8));
            } else {
                rgb[i] = 0;
                b &= ~(1 << (i % 8));
                allvalid = 0;
            }
            if ((i % 8) == 7) {
                *--pValid = b;
                /* b = 0; not needed as each bit is explicitly set */
            }
        }

        if (allocAllGray && (aData->awtImage->clrdata.grayscale == 0)) {
            /*
              Fix for 4351638 - Gray scale HW mode on Dome frame buffer
                                crashes VM on Solaris.
              It is possible for an X11 frame buffer to advertise a
              PseudoColor visual, but to force all allocated colormap
              entries to be gray colors.  The Dome card does this when the
              HW is jumpered for a grayscale monitor, but the default
              visual is set to PseudoColor.  In that case awtJNI_GetColorModel
              will be called with aData->awtImage->clrdata.grayscale == 0,
              but the IndexColorModel created below will detect that only
              gray colors exist and expect the inverse gray LUT to exist.
              So above when filling the hR, hG, and hB arrays we detect
              whether all allocated colors are gray.  If so, but
              aData->awtImage->clrdata.grayscale == 0, we fall into this
              code to set aData->awtImage->clrdata.grayscale = 1 and do
              other things needed for the grayscale case.
             */

            int i;
            int g;
            ColorEntry *p;

            aData->awtImage->clrdata.grayscale = 1;

            aData->color_data->img_grays =
                (unsigned char *)calloc(256, sizeof(unsigned char));

            if (aData->color_data->img_grays == NULL) {
                (*env)->PopLocalFrame(env, 0);
                return NULL;
            }

            for (g = 0; g < 256; g++) {
                int mindist, besti;
                int d;

                p = aData->color_data->awt_Colors;
                mindist = 256;
                besti = 0;
                for (i = 0 ; i < paletteSize; i++, p++) {
                    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;
                        }
                    }
                }

                aData->color_data->img_grays[g] = besti;
            }

            for (i = 0; i < 256; i++) {
                img_bwgamma[i] = i;    /* REMIND: what is img_bwgamma?
                                        *         is it still used anywhere?
                                        */
            }
        }

        if (aData->awtImage->clrdata.grayscale) {
            int i;
            ColorEntry *p;

            /* For purposes of creating an IndexColorModel, use
               transparent black for non-allocated or non-gray colors.
             */
            p = aData->color_data->awt_Colors;
            b = 0;
            pValid = &valid[sizeof(valid)];
            for (i = 0; i < paletteSize; i++, p++) {
                if ((p->flags != ALLOCATED_COLOR) ||
                    (p->r != p->g || p->g != p->b))
                {
                    rgb[i] = 0;
                    b &= ~(1 << (i % 8));
                    allvalid = 0;
                } else {
                    b |= (1 << (i % 8));
                }
                if ((i % 8) == 7) {
                    *--pValid = b;
                    /* b = 0; not needed as each bit is explicitly set */
                }
            }

            if (aData->color_data->pGrayInverseLutData == NULL) {
                /* Compute the inverse gray LUT for this aData->color_data
                   struct, if not already computed.
                 */
                initInverseGrayLut(rgb, aData->awt_num_colors,
                                   aData->color_data);
            }
        }

        if (!allvalid) {
            jobject bArray = (*env)->NewByteArray(env, sizeof(valid));
            if (bArray == NULL)
            {
                (*env)->PopLocalFrame(env, 0);
                return NULL;
            }
            else
            {
                (*env)->SetByteArrayRegion(env, bArray, 0, sizeof(valid),
                                           valid);
            }
            validBits = JNU_NewObjectByName(env,
                                            "java/math/BigInteger",
                                            "([B)V", bArray);
            if (validBits == NULL)
            {
                (*env)->PopLocalFrame(env, 0);
                return NULL;
            }
        }

        hArray = (*env)->NewIntArray(env, paletteSize);
        if (hArray == NULL)
        {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }
        else
        {
            (*env)->SetIntArrayRegion(env, hArray, 0, paletteSize, rgb);
        }

        if (aData->awt_visInfo.depth == 8) {
            awt_colormodel =
                JNU_NewObjectByName(env,
                                    "java/awt/image/IndexColorModel",
                                    "(II[IIILjava/math/BigInteger;)V",
                                    8, 256, hArray, 0,
                                    java_awt_image_DataBuffer_TYPE_BYTE,
                                    validBits);
        } else {
            awt_colormodel =
                JNU_NewObjectByName(env,
                                    "java/awt/image/IndexColorModel",
                                    "(II[IIILjava/math/BigInteger;)V",
                                    12, 4096, hArray, 0,
                                    java_awt_image_DataBuffer_TYPE_USHORT,
                                    validBits);
        }

        if (awt_colormodel == NULL)
        {
            (*env)->PopLocalFrame(env, 0);
            return NULL;
        }

        /* Set pData field of ColorModel to point to ColorData */
        JNU_SetLongFieldFromPtr(env, awt_colormodel, g_CMpDataID,
                                aData->color_data);

    }

    return (*env)->PopLocalFrame(env, awt_colormodel);
}
#endif /* !HEADLESS */

extern jfieldID colorValueID;

#ifndef HEADLESS
int awtJNI_GetColor(JNIEnv *env,jobject this)
{
    /* REMIND: should not be defaultConfig. */
    return awtJNI_GetColorForVis (env, this, getDefaultConfig(DefaultScreen(awt_display)));
}

int awtJNI_GetColorForVis (JNIEnv *env,jobject this, AwtGraphicsConfigDataPtr awt_data)
{
    int col;
    jclass SYSCLR_class;

    if (!JNU_IsNull(env,this))
    {
        SYSCLR_class = (*env)->FindClass(env, "java/awt/SystemColor");

        if ((*env)->IsInstanceOf(env, this, SYSCLR_class)) {
                /* SECURITY: This is safe, because there is no way
                 *           for client code to insert an object
                 *           that is a subclass of SystemColor
                 */
                col = (int) JNU_CallMethodByName(env
                                          ,NULL
                                          ,this
                                          ,"getRGB"
                                          ,"()I").i;
        } else {
                col = (int)(*env)->GetIntField(env,this,colorValueID);
        }

        if (awt_data->awt_cmap == (Colormap) NULL) {
            awtJNI_CreateColorData (env, awt_data, 1);
        }

        col = awt_data->AwtColorMatch(red(col), green(col), blue(col),
                                      awt_data);
        return col;
    }

    return 0;
}

void
awt_allocate_systemrgbcolors (jint *rgbColors, int num_colors,
                              AwtGraphicsConfigDataPtr awtData) {
    int i, pixel;
    for (i = 0; i < num_colors; i++)
        pixel = alloc_col (awt_display, awtData->awt_cmap, red (rgbColors [i]),
                           green (rgbColors [i]), blue (rgbColors [i]), -1,
                           awtData);
}

int
awtCreateX11Colormap(AwtGraphicsConfigDataPtr adata) {
    int screen = adata->awt_visInfo.screen;
    Colormap cmap = (Colormap)NULL;

    if (adata->awt_visInfo.visual == DefaultVisual(awt_display, screen)) {
        cmap = DefaultColormap(awt_display, screen);
    } else {
        Window root = RootWindow(awt_display, screen);

        if (adata->awt_visInfo.visual->class % 2) {
            Atom actual_type;
            int actual_format;
            unsigned long nitems, bytes_after;
            XStandardColormap *scm;

            XGetWindowProperty (awt_display, root, XA_RGB_DEFAULT_MAP,
                                0L, 1L, False, AnyPropertyType, &actual_type,
                                &actual_format, &nitems, &bytes_after,
                                (unsigned char **) &scm);

            XGetWindowProperty (awt_display, root, XA_RGB_DEFAULT_MAP, 0L,
                                bytes_after/4 + 1, False, AnyPropertyType,
                                &actual_type, &actual_format, &nitems,
                                &bytes_after, (unsigned char **) &scm);

            nitems /= (sizeof (XStandardColormap)/4);
            for (; nitems > 0; ++scm, --nitems)
                if (scm->visualid == adata->awt_visInfo.visualid) {
                    cmap = scm->colormap;
                    break;
                }
        }
        if (!cmap) {
            cmap = XCreateColormap (awt_display, root,
                                    adata->awt_visInfo.visual,
                                    AllocNone);
        }
    }

    adata->awt_cmap = cmap;
    if (!awt_allocate_colors(adata)) {
        XFreeColormap(awt_display, adata->awt_cmap);
        adata->awt_cmap = (Colormap)NULL;
        return 0;
    }
    return 1;
}

void
awtJNI_CreateColorData(JNIEnv *env, AwtGraphicsConfigDataPtr adata,
                       int lock) {

    /* Create Colormap */
    if (lock) {
        AWT_LOCK ();
    }

    awtCreateX11Colormap(adata);

    /* If depth is 8, allocate system colors also...  Here
     * we just get the array of System Colors and allocate
     * it which may be a bit wasteful (if only some were
     * changed). But we don't know which ones were changed
     * and alloc-ing a pixel that is already allocated won't
     * hurt. */

    if (adata->awt_depth == 8 ||
        (adata->awt_depth == 12 && adata->awt_visInfo.class == PseudoColor))
    {
        jint colorVals [java_awt_SystemColor_NUM_COLORS];
        jclass sysColors;
        jfieldID colorID;
        jintArray colors;

        /* Unlock now to initialize the SystemColor class */
        if (lock) {
            AWT_UNLOCK ();
        }
        sysColors = (*env)->FindClass (env, "java/awt/SystemColor");
        if (lock) {
            AWT_LOCK ();
        }
        colorID = (*env)->GetStaticFieldID (env, sysColors,
                                                   "systemColors",
                                                   "[I");

        colors = (jintArray) (*env)->GetStaticObjectField
                                                (env, sysColors, colorID);

        (*env)->GetIntArrayRegion (env, colors, 0,
                                     java_awt_SystemColor_NUM_COLORS,
                                     (jint *) colorVals);

        awt_allocate_systemrgbcolors (colorVals,
                        (java_awt_SystemColor_NUM_COLORS - 1), adata);

    }

    if (lock) {
        AWT_UNLOCK ();
    }
}

#endif /* !HEADLESS */
