--- /dev/null
+
+/*
+ * bltColor.c --
+ *
+ * This module contains routines for color allocation strategies
+ * used with color images in the BLT toolkit.
+ *
+ * Copyright 1997-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ */
+
+/*
+ * Color strategies of 8-bit visuals:
+ *
+ * Try to "best" represent an N-color image into 8-bit (256 color)
+ * colormap. The simplest method is use the high bits of each RGB
+ * value (3 bits for red and green, 2 bits for blue). But this
+ * produces lots of contouring since the distribution of colors tends
+ * to be clustered. Other problems: probably can't allocate even 256
+ * colors. Other applications will have already taken some color
+ * slots. Furthermore, we might be displaying several images, and we
+ * can't assume that all images are representative of the colors used.
+ *
+ * If we use a private colormap, we may want to allocate some number
+ * of colors from the default colormap to prevent flashing when
+ * colormaps are switched.
+ *
+ * Switches:
+ *
+ * -exact boolean Try to match the colors of the image rather
+ * then generating a "best" color ramp.
+ *
+ * -threshold value Maximum average error. Indicates how far
+ * to reduce the quantized color palette.
+ *
+ * -tolerance value Allow colors within this distance to match.
+ * This will weight allocation towards harder
+ * to match colors, rather than the most
+ * frequent.
+ *
+ * -mincolors number Minimum number of reduced quantized colors.
+ * or color ramp.
+ *
+ * -dither boolean Turn on/off Floyd-Steinberg dithering.
+ *
+ * -keep number Hint to keep the first N colors in the
+ * in the default colormap. This only applies to
+ * private colormaps.
+ *
+ * -ramp number Number of colors to use in a linear ramp.
+ *
+ */
+
+#include "bltInt.h"
+
+#ifndef WIN32
+
+#include "bltHash.h"
+#include "bltImage.h"
+
+#define NCOLORS 256
+
+
+static void
+GetPaletteSizes(nColors, nRedsPtr, nGreensPtr, nBluesPtr)
+ int nColors; /* Number of colors requested. */
+ unsigned int *nRedsPtr; /* (out) Number of red components. */
+ unsigned int *nGreensPtr; /* (out) Number of green components. */
+ unsigned int *nBluesPtr; /* (out) Number of blue components. */
+{
+ unsigned int nBlues, nReds, nGreens;
+
+ assert(nColors > 1);
+ nBlues = nReds = nGreens = 0;
+ while ((nBlues * nBlues * nBlues) <= nColors) {
+ nBlues++;
+ }
+ nBlues--;
+ while ((nReds * nReds * nBlues) <= nColors) {
+ nReds++;
+ }
+ nReds--;
+ nGreens = nColors / (nBlues * nReds);
+
+ *nRedsPtr = nReds;
+ *nGreensPtr = nGreens;
+ *nBluesPtr = nBlues;
+}
+
+static void
+BuildColorRamp(palettePtr, nColors)
+ Pix32 *palettePtr;
+ int nColors;
+{
+ register unsigned int r, g, b;
+ unsigned int short red, green, blue;
+ unsigned int nReds, nGreens, nBlues;
+
+ GetPaletteSizes(nColors, &nReds, &nGreens, &nBlues);
+ for (r = 0; r < nReds; r++) {
+ red = (r * USHRT_MAX) / (nReds - 1);
+ for (g = 0; g < nGreens; g++) {
+ green = (g * USHRT_MAX) / (nGreens - 1);
+ for (b = 0; b < nBlues; b++) {
+ blue = (b * USHRT_MAX) / (nBlues - 1);
+ palettePtr->Red = red;
+ palettePtr->Green = green;
+ palettePtr->Blue = blue;
+ palettePtr++;
+ }
+ }
+ }
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * QueryColormap --
+ *
+ * This is for psuedo-color displays only. Fills an array or
+ * XColors with the color values (RGB and pixel) currently
+ * allocated in the colormap.
+ *
+ * Results:
+ * The number of colors allocated is returned. The array "colorArr"
+ * will contain the XColor values of each color in the colormap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+QueryColormap(display, colorMap, mapColors, numMapColorsPtr)
+ Display *display;
+ Colormap colorMap;
+ XColor mapColors[];
+ int *numMapColorsPtr;
+{
+ unsigned long int pixelValues[NCOLORS];
+ int numAvail, numMapColors;
+ register int i;
+ register XColor *colorPtr;
+ register unsigned long *indexPtr;
+ int inUse[NCOLORS];
+
+ /* Initially, we assume all color cells are allocated. */
+ memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+
+ /*
+ * Start allocating color cells. This will tell us which color cells
+ * haven't already been allocated in the colormap. We'll release the
+ * cells as soon as we find out how many there are.
+ */
+ numAvail = 0;
+ for (indexPtr = pixelValues, i = 0; i < NCOLORS; i++, indexPtr++) {
+ if (!XAllocColorCells(display, colorMap, False, NULL, 0, indexPtr, 1)) {
+ break;
+ }
+ inUse[*indexPtr] = TRUE;/* Indicate the cell is unallocated */
+ numAvail++;
+ }
+ XFreeColors(display, colorMap, pixelValues, numAvail, 0);
+
+ /*
+ * Put the indices of the cells already allocated into a color array.
+ * We'll use the array to query the RGB values of the allocated colors.
+ */
+ numMapColors = 0;
+ colorPtr = mapColors;
+ for (i = 0; i < NCOLORS; i++) {
+ if (!inUse[i]) {
+ colorPtr->pixel = i;
+ colorPtr->flags = (DoRed | DoGreen | DoBlue);
+ colorPtr++, numMapColors++;
+ }
+ }
+ XQueryColors(display, colorMap, mapColors, numMapColors);
+ *numMapColorsPtr = numMapColors;
+#ifdef notdef
+ fprintf(stderr, "Number of colors (allocated/free) %d/%d\n", numMapColors,
+ numAvail);
+#endif
+ return numAvail;
+}
+
+static void
+FindClosestColor(colorPtr, mapColors, numMapColors)
+ ColorInfo *colorPtr;
+ XColor mapColors[];
+ int numMapColors;
+{
+ double r, g, b;
+ register int i;
+ double dist, min;
+ XColor *lastMatch;
+ register XColor *mapColorPtr;
+
+ min = DBL_MAX; /* Any color is closer. */
+ lastMatch = NULL;
+
+ /* Linear search of color */
+
+ mapColorPtr = mapColors;
+ for (i = 0; i < numMapColors; i++, mapColorPtr++) {
+ r = (double)mapColorPtr->red - (double)colorPtr->exact.red;
+ g = (double)mapColorPtr->green - (double)colorPtr->exact.green;
+ b = (double)mapColorPtr->blue - (double)colorPtr->exact.blue;
+
+ dist = (r * r) + (b * b) + (g * g);
+ if (dist < min) {
+ min = dist;
+ lastMatch = mapColorPtr;
+ }
+ }
+ colorPtr->best = *lastMatch;
+ colorPtr->best.flags = (DoRed | DoGreen | DoBlue);
+ colorPtr->error = (float)sqrt(min);
+}
+
+static int
+CompareColors(a, b)
+ void *a, *b;
+{
+ ColorInfo *i1Ptr, *i2Ptr;
+
+ i1Ptr = *(ColorInfo **) a;
+ i2Ptr = *(ColorInfo **) b;
+ if (i2Ptr->error > i1Ptr->error) {
+ return 1;
+ } else if (i2Ptr->error < i1Ptr->error) {
+ return -1;
+ }
+ return 0;
+}
+
+static float
+MatchColors(colorTabPtr, rgbPtr, numColors, numAvailColors, numMapColors,
+ mapColors)
+ struct ColorTableStruct *colorTabPtr;
+ Pix32 *rgbPtr;
+ int numColors;
+ int numAvailColors;
+ int numMapColors;
+ XColor mapColors[NCOLORS];
+{
+ int numMatched;
+ float sum;
+ register int i;
+ register ColorInfo *colorPtr;
+
+ /*
+ * For each quantized color, compute and store the error (i.e
+ * the distance from a color that's already been allocated).
+ * We'll use this information to sort the colors based upon how
+ * badly they match and their frequency to the color image.
+ */
+ colorPtr = colorTabPtr->colorInfo;
+ for (i = 0; i < numColors; i++, colorPtr++, rgbPtr++) {
+ colorPtr->index = i;
+ colorTabPtr->sortedColors[i] = colorPtr;
+ colorPtr->exact.red = rgbPtr->Red;
+ colorPtr->exact.green = rgbPtr->Green;
+ colorPtr->exact.blue = rgbPtr->Blue;
+ colorPtr->exact.flags = (DoRed | DoGreen | DoBlue);
+ FindClosestColor(colorPtr, mapColors, numMapColors);
+ }
+
+ /* Sort the colors, first by frequency (most to least), then by
+ * matching error (worst to best).
+ */
+ qsort(colorTabPtr->sortedColors, numColors, sizeof(ColorInfo *),
+ (QSortCompareProc *)CompareColors);
+
+ for (i = 0; i < numColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ fprintf(stderr, "%d. %04x%04x%04x / %04x%04x%04x = %f (%d)\n", i,
+ colorPtr->exact.red, colorPtr->exact.green, colorPtr->exact.blue,
+ colorPtr->best.red, colorPtr->best.green, colorPtr->best.blue,
+ colorPtr->error, colorPtr->freq);
+ }
+ sum = 0.0;
+ numMatched = 0;
+ for (i = numAvailColors; i < numColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ sum += colorPtr->error;
+ numMatched++;
+ }
+ if (numMatched > 0) {
+ sum /= numMatched;
+ }
+ return sum;
+}
+
+
+static int
+AllocateColors(nImageColors, colorTabPtr, matchOnly)
+ int nImageColors;
+ struct ColorTableStruct *colorTabPtr;
+ int matchOnly;
+{
+ register int i;
+ register ColorInfo *colorPtr;
+ unsigned long int pixelValue;
+
+ for (i = 0; i < nImageColors; i++) {
+ colorPtr = colorTabPtr->sortedColors[i];
+ if (matchOnly) {
+ XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+ &colorPtr->best);
+ pixelValue = colorPtr->best.pixel;
+ } else {
+ colorPtr->allocated = XAllocColor(colorTabPtr->display,
+ colorTabPtr->colorMap, &colorPtr->exact);
+
+ if (colorPtr->allocated) {
+ pixelValue = colorPtr->exact.pixel;
+ } else {
+ XAllocColor(colorTabPtr->display, colorTabPtr->colorMap,
+ &colorPtr->best);
+ pixelValue = colorPtr->best.pixel;
+ }
+ }
+ colorTabPtr->pixelValues[colorPtr->index] = pixelValue;
+ }
+ colorTabPtr->nPixels = nImageColors;
+ return 1;
+}
+
+ColorTable
+Blt_CreateColorTable(tkwin)
+ Tk_Window tkwin;
+{
+ XVisualInfo visualInfo, *visualInfoPtr;
+ int nVisuals;
+ Visual *visualPtr;
+ Display *display;
+ struct ColorTableStruct *colorTabPtr;
+
+ display = Tk_Display(tkwin);
+ visualPtr = Tk_Visual(tkwin);
+
+ colorTabPtr = Blt_Calloc(1, sizeof(struct ColorTableStruct));
+ assert(colorTabPtr);
+ colorTabPtr->display = Tk_Display(tkwin);
+ colorTabPtr->colorMap = Tk_Colormap(tkwin);
+
+ visualInfo.screen = Tk_ScreenNumber(tkwin);
+ visualInfo.visualid = XVisualIDFromVisual(visualPtr);
+ visualInfoPtr = XGetVisualInfo(display, VisualScreenMask | VisualIDMask,
+ &visualInfo, &nVisuals);
+
+ colorTabPtr->visualInfo = *visualInfoPtr;
+ XFree(visualInfoPtr);
+
+ return colorTabPtr;
+}
+
+void
+Blt_FreeColorTable(colorTabPtr)
+ struct ColorTableStruct *colorTabPtr;
+{
+ if (colorTabPtr == NULL) {
+ return;
+ }
+ if (colorTabPtr->nPixels > 0) {
+ XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+ }
+ Blt_Free(colorTabPtr);
+}
+
+extern int redAdjust, greenAdjust, blueAdjust;
+extern int redMaskShift, greenMaskShift, blueMaskShift;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_DirectColorTable --
+ *
+ * Creates a color table using the DirectColor visual. We try
+ * to allocate colors across the color spectrum.
+ *
+ * Results:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+ColorTable
+Blt_DirectColorTable(interp, tkwin, image)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Blt_ColorImage image;
+{
+ struct ColorTableStruct *colorTabPtr;
+ Visual *visualPtr;
+ Display *display;
+ XColor color;
+ int nr, ng, nb;
+ int rBand, gBand, bBand;
+ int rLast, gLast, bLast;
+ unsigned int r, g, b;
+ unsigned int value;
+ register int i;
+
+ display = Tk_Display(tkwin);
+ visualPtr = Tk_Visual(tkwin);
+
+ colorTabPtr = Blt_CreateColorTable(tkwin);
+ /*
+ * Compute the number of distinct colors in each band
+ */
+ nr = ((unsigned int)visualPtr->red_mask >> redMaskShift) + 1;
+ ng = ((unsigned int)visualPtr->green_mask >> greenMaskShift) + 1;
+ nb = ((unsigned int)visualPtr->blue_mask >> blueMaskShift) + 1;
+
+#ifdef notdef
+ assert((nr <= visualPtr->map_entries) && (ng <= visualPtr->map_entries) &&
+ (nb <= visualPtr->map_entries));
+#endif
+ rBand = NCOLORS / nr;
+ gBand = NCOLORS / ng;
+ bBand = NCOLORS / nb;
+
+ retry:
+ color.flags = (DoRed | DoGreen | DoBlue);
+ rLast = gLast = bLast = 0;
+ r = g = b = 0;
+ for (i = 0; i < visualPtr->map_entries; i++) {
+ if (rLast < NCOLORS) {
+ r = rLast + rBand;
+ if (r > NCOLORS) {
+ r = NCOLORS;
+ }
+ }
+ if (gLast < NCOLORS) {
+ g = gLast + gBand;
+ if (g > NCOLORS) {
+ g = NCOLORS;
+ }
+ }
+ if (bLast < NCOLORS) {
+ b = bLast + bBand;
+ if (b > NCOLORS) {
+ b = NCOLORS;
+ }
+ }
+ color.red = (r - 1) * (NCOLORS + 1);
+ color.green = (g - 1) * (NCOLORS + 1);
+ color.blue = (b - 1) * (NCOLORS + 1);
+
+ if (!XAllocColor(display, colorTabPtr->colorMap, &color)) {
+ XFreeColors(display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, i, 0);
+ if ((colorTabPtr->flags & PRIVATE_COLORMAP) == 0) {
+ /*
+ * If we can't allocate a color in the default
+ * colormap, try again, this time with a private
+ * colormap.
+ */
+ fprintf(stderr, "Need to allocate private colormap\n");
+ colorTabPtr->colorMap = Tk_GetColormap(interp, tkwin, ".");
+
+ XSetWindowColormap(display, Tk_WindowId(tkwin),
+ colorTabPtr->colorMap);
+ colorTabPtr->flags |= PRIVATE_COLORMAP;
+ goto retry;
+ }
+#ifdef notdef
+ fprintf(stderr, "Failed to allocate after %d colors\n", i);
+#endif
+ Blt_Free(colorTabPtr);
+ return NULL; /* Ran out of colors in private map? */
+ }
+ colorTabPtr->pixelValues[i] = color.pixel;
+ /*
+ * Fill in pixel values for each band at this intensity
+ */
+ value = color.pixel & visualPtr->red_mask;
+ while (rLast < r) {
+ colorTabPtr->red[rLast++] = value;
+ }
+ value = color.pixel & visualPtr->green_mask;
+ while (gLast < g) {
+ colorTabPtr->green[gLast++] = value;
+ }
+ value = color.pixel & visualPtr->blue_mask;
+ while (bLast < b) {
+ colorTabPtr->blue[bLast++] = value;
+ }
+ }
+ colorTabPtr->nPixels = i;
+ return colorTabPtr;
+}
+
+/*
+ * First attempt:
+ * Allocate colors all the colors in the image (up to NCOLORS). Bail out
+ * on the first failure or if we need more than NCOLORS.
+ */
+static int
+GetUniqueColors(image)
+ Blt_ColorImage image;
+{
+ register int i, nColors;
+ register Pix32 *pixelPtr;
+ Pix32 color;
+ Blt_HashEntry *hPtr;
+ int isNew, nPixels;
+ int refCount;
+ Blt_HashTable colorTable;
+
+ Blt_InitHashTable(&colorTable, BLT_ONE_WORD_KEYS);
+
+ nPixels = Blt_ColorImageWidth(image) * Blt_ColorImageHeight(image);
+ nColors = 0;
+ pixelPtr = Blt_ColorImageBits(image);
+ for (i = 0; i < nPixels; i++, pixelPtr++) {
+ color.value = pixelPtr->value;
+ color.Alpha = 0xFF; /* Ignore alpha-channel values */
+ hPtr = Blt_CreateHashEntry(&colorTable, (char *)color.value, &isNew);
+ if (isNew) {
+ refCount = 1;
+ nColors++;
+ } else {
+ refCount = (int)Blt_GetHashValue(hPtr);
+ refCount++;
+ }
+ Blt_SetHashValue(hPtr, (ClientData)refCount);
+ }
+ Blt_DeleteHashTable(&colorTable);
+ return nColors;
+}
+
+#define Blt_DefaultColormap(tkwin) \
+ DefaultColormap(Tk_Display(tkwin), Tk_ScreenNumber(tkwin))
+
+
+static void
+PrivateColormap(interp, colorTabPtr, image, tkwin)
+ Tcl_Interp *interp;
+ struct ColorTableStruct *colorTabPtr;
+ Blt_ColorImage image;
+ Tk_Window tkwin;
+{
+ int keepColors = 0;
+ register int i;
+ XColor usedColors[NCOLORS];
+ int nFreeColors, nUsedColors;
+ Colormap colorMap;
+ int inUse[NCOLORS];
+ XColor *colorPtr;
+ XColor *imageColors;
+
+ /*
+ * Create a private colormap if one doesn't already exist for the
+ * window.
+ */
+
+ colorTabPtr->colorMap = colorMap = Tk_Colormap(tkwin);
+
+ nUsedColors = 0; /* Number of colors allocated */
+
+ if (colorTabPtr->nPixels > 0) {
+ XFreeColors(colorTabPtr->display, colorTabPtr->colorMap,
+ colorTabPtr->pixelValues, colorTabPtr->nPixels, 0);
+ }
+ nFreeColors = QueryColormap(colorTabPtr->display, colorMap, usedColors,
+ &nUsedColors);
+ memset((char *)inUse, 0, sizeof(int) * NCOLORS);
+ if ((nUsedColors == 0) && (keepColors > 0)) {
+
+ /*
+ * We're starting with a clean colormap so find out what colors
+ * have been used in the default colormap.
+ */
+
+ nFreeColors = QueryColormap(colorTabPtr->display,
+ Blt_DefaultColormap(tkwin), usedColors, &nUsedColors);
+
+ /*
+ * Copy a number of colors from the default colormap into the private
+ * colormap. We can assume that this is the working set from most
+ * (non-image related) applications. While this doesn't stop our
+ * image from flashing and looking dumb when colormaps are swapped
+ * in and out, at least everything else should remain unaffected.
+ */
+
+ if (nUsedColors > keepColors) {
+ nUsedColors = keepColors;
+ }
+ /*
+ * We want to allocate colors in the same ordering as the old colormap,
+ * and we can't assume that the colors in the old map were contiguous.
+ * So mark the colormap locations (i.e. pixels) that we find in use.
+ */
+
+ }
+ for (colorPtr = usedColors, i = 0; i < nUsedColors; i++, colorPtr++) {
+ inUse[colorPtr->pixel] = TRUE;
+ }
+
+ /*
+ * In an "exact" colormap, we try to allocate as many of colors from the
+ * image as we can fit. If necessary, we'll cheat and reduce the number
+ * of colors by quantizing.
+ */
+ imageColors = usedColors + nUsedColors;
+
+ Tk_SetWindowColormap(tkwin, colorMap);
+}
+
+ColorTable
+Blt_PseudoColorTable(interp, tkwin, image)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ Blt_ColorImage image;
+{
+ struct ColorTableStruct *colorTabPtr;
+ Colormap defColorMap;
+ int usePrivate;
+
+ colorTabPtr = Blt_CreateColorTable(tkwin);
+ defColorMap = DefaultColormap(colorTabPtr->display, Tk_ScreenNumber(tkwin));
+ if (colorTabPtr->colorMap == defColorMap) {
+ fprintf(stderr, "Using default colormap\n");
+ }
+ /* All other visuals use an 8-bit colormap */
+ colorTabPtr->lut = Blt_Malloc(sizeof(unsigned int) * 33 * 33 * 33);
+ assert(colorTabPtr->lut);
+
+ usePrivate = TRUE;
+ if (usePrivate) {
+ PrivateColormap(interp, colorTabPtr, image, tkwin);
+ } else {
+#ifdef notdef
+ ReadOnlyColormap(colorTabPtr, image, tkwin);
+#endif
+ }
+ return colorTabPtr;
+}
+
+#ifdef notdef
+
+static void
+ConvoleColorImage(srcImage, destImage, kernelPtr)
+ Blt_ColorImage srcImage, destImage;
+ ConvoleKernel *kernelPtr;
+{
+ Pix32 *srcPtr, *destPtr;
+ Pix32 *src[MAXROWS];
+ register int x, y, i, j;
+ int red, green, blue;
+
+ /* i = 0 case, ignore left column of pixels */
+
+ srcPtr = Blt_ColorImageBits(srcImage);
+ destPtr = Blt_ColorImageBits(destImage);
+
+ width = Blt_ColorImageWidth(srcImage);
+ height = Blt_ColorImageHeight(srcImage);
+
+ yOffset = kernelPtr->height / 2;
+ xOffset = kernelPtr->width / 2;
+ for (y = yOffset; y < (height - yOffset); y++) {
+ /* Set up pointers to individual rows */
+ for (i = 0; i < kernelPtr->height; i++) {
+ src[i] = srcPtr + (i * width);
+ }
+ for (x = xOffset; x < (width - xOffset); x++) {
+ red = green = blue = 0;
+ kernPtr = kernelPtr->values;
+ for (i = 0; i < kernelPtr->height; i++) {
+ for (j = 0; j < kernelPtr->width; j++) {
+ red += *valuePtr * src[i][j].Red;
+ green += *valuePtr * src[i][j].Green;
+ blue += *valuePtr * src[i][j].Blue;
+ valuePtr++;
+ }
+ }
+ destPtr->Red = red / kernelPtr->sum;
+ destPtr->Green = green / kernelPtr->sum;
+ destPtr->Blue = blue / kernelPtr->sum;
+ destPtr++;
+ }
+ srcPtr += width;
+ }
+ sum = bot[0].Red +
+ red = bot[0].Red + bot[1].Red + mid[1].Red + top[0].Red + top[1].Red;
+ green = bot[0].Green + bot[1].Green + mid[1].Green + top[0].Green +
+ top[1].Green;
+ blue = bot[0].Blue + bot[1].Blue + mid[1].Blue + top[0].Blue + top[1].Blue;
+ error = (red / 5) - mid[0].Red;
+ redVal = mid[0].Red - (error * blend / blend_divisor);
+ error = (green / 5) - mid[0].Green;
+ greenVal = mid[0].Green - (error * blend / blend_divisor);
+ error = (blue / 5) - mid[0].Blue;
+ blueVal = mid[0].Blue - (error * blend / blend_divisor);
+
+ out[0].Red = CLAMP(redVal);
+ out[0].Green = CLAMP(greenVal);
+ out[0].Blue = CLAMP(blueVal);
+
+ for (i = 1; i < (width - 1); i++) {
+ for (chan = 0; chan < 3; chan++) {
+ total = bot[chan][i - 1] + bot[chan][i] + bot[chan][i + 1] +
+ mid[chan][i - 1] + mid[chan][i + 1] +
+ top[chan][i - 1] + top[chan][i] + top[chan][i + 1];
+ avg = total >> 3; /* divide by 8 */
+ diff = avg - mid[chan][i];
+ result = mid[chan][i] - (diff * blend / blend_divisor);
+ out[chan][i] = CLAMP(result);
+ }
+ }
+ /* i = in_hdr.xmax case, ignore right column of pixels */
+ for (chan = 0; chan < 3; chan++) {
+ total = bot[chan][i - 1] + bot[chan][i] +
+ mid[chan][i - 1] +
+ top[chan][i - 1] + top[chan][i];
+ avg = total / 5;
+ diff = avg - mid[chan][i];
+ result = mid[chan][i] - (diff * blend / blend_divisor);
+ out[chan][i] = CLAMP(result);
+ }
+}
+
+static void
+DitherRow(srcImage, destImage, lastRow, curRow)
+ Blt_ColorImage srcImage, destImage;
+ int width, height;
+ int bottom, top;
+{
+ int width, height;
+
+ width = Blt_ColorImageWidth(srcImage);
+ topPtr = Blt_ColorImageBits(destPtr) + (width * row);
+ rowPtr = topPtr + width;
+ botPtr = rowPtr + width;
+
+ for (x = 0; x < width; x++) {
+
+ /* Clamp current error entry */
+
+ midPtr->red = CLAMP(midPtr->red);
+ midPtr->blue = CLAMP(midPtr->blue);
+ midPtr->green = CLAMP(midPtr->green);
+
+ r = (midPtr->red >> 3) + 1;
+ g = (midPtr->green >> 3) + 1;
+ b = (midPtr->blue >> 3) + 1;
+ index = colorTabPtr->lut[r][g][b];
+
+ redVal = midPtr->red * (NCOLORS + 1);
+ greenVal = midPtr->green * (NCOLORS + 1);
+ blueVal = midPtr->blue * (NCOLORS + 1);
+
+ error = colorVal - colorMap[index].red;
+ if (x < 511) {
+ currRow[x + 1].Red = currRow[x + 1].Red + 7 * error / 16;
+ nextRow[x + 1].Red = nextRow[x + 1].Red + error / 16;
+ }
+ nextRow[x].Red = nextRow[x].Red + 5 * error / 16;
+ if (x > 0) {
+ nextRow[x - 1].Red = nextRow[x - 1].Red + 3 * error / 16;
+ }
+ error = row[x][c] - colormap[index][c];
+
+ value = srcPtr->channel[i] * error[i];
+ value = CLAMP(value);
+ destPtr->channel[i] = value;
+
+ /* Closest pixel */
+ pixel = PsuedoColorPixel();
+ error[RED] = colorPtr->Red - srcPtr->Red * (NCOLORS + 1);
+
+ /* translate pixel to colorInfoPtr to get error */
+ colorTabPtr->lut[r][g][b];
+ colorPtr = PixelToColorInfo(pixel);
+ error = colorPtr->error;
+
+ register rle_pixel *optr;
+ register int j;
+ register short *thisptr, *nextptr = NULL;
+ int chan;
+ static int nchan = 0;
+ int lastline = 0, lastpixel;
+ static int *cval = 0;
+ static rle_pixel *pixel = 0;
+
+ if (nchan != in_hdr->ncolors)
+ if (cval) {
+ Blt_Free(cval);
+ Blt_Free(pixel);
+ }
+ nchan = in_hdr->ncolors;
+ if (!cval) {
+ if ((cval = Blt_Malloc(nchan * sizeof(int))) == 0)
+ malloc_ERR;
+ if ((pixel = Blt_Malloc(nchan * sizeof(rle_pixel))) == 0)
+ malloc_ERR;
+ }
+ optr = outrow[RLE_RED];
+
+ thisptr = row_top;
+ if (row_bottom)
+ nextptr = row_bottom;
+ else
+ lastline = 1;
+
+ for (x = 0; x < width; x++) {
+ int cmap_index = 0;
+
+ lastpixel = (x == (width - 1));
+ val = srcPtr->Red;
+
+ for (chan = 0; chan < 3; chan++) {
+ cval[chan] = *thisptr++;
+
+ /*
+ * Current channel value has been accumulating error,
+ * it could be out of range.
+ */
+ if (cval[chan] < 0)
+ cval[chan] = 0;
+ else if (cval[chan] > 255)
+ cval[chan] = 255;
+
+ pixel[chan] = cval[chan];
+ }
+
+ /* find closest color */
+ find_closest(map, nchan, maplen, pixel, &cmap_index);
+ *optr++ = cmap_index;
+
+ /* thisptr is now looking at pixel to the right of current pixel
+ * nextptr is looking at pixel below current pixel
+ * So, increment thisptr as stuff gets stored. nextptr gets moved
+ * by one, and indexing is done +/- nchan.
+ */
+ for (chan = 0; chan < nchan; chan++) {
+ cval[chan] -= map[chan][cmap_index];
+
+ if (!lastpixel) {
+ thisptr[chan] += cval[chan] * 7 / 16;
+ }
+ if (!lastline) {
+ if (j != 0) {
+ nextptr[-nchan] += cval[chan] * 3 / 16;
+ }
+ nextptr[0] += cval[chan] * 5 / 16;
+ if (!lastpixel) {
+ nextptr[nchan] += cval[chan] / 16;
+ }
+ nextptr++;
+ }
+ }
+ }
+ }
+}
+
+/********************************************/
+static Blt_ColorImage
+DoColorDither(pic24, pic8, w, h, rmap, gmap, bmap, rdisp, gdisp, bdisp, maplen)
+ byte *pic24, *pic8, *rmap, *gmap, *bmap, *rdisp, *gdisp, *bdisp;
+ int w, h, maplen;
+{
+ /* takes a 24 bit picture, of size w*h, dithers with the colors in
+ rdisp, gdisp, bdisp (which have already been allocated),
+ and generates an 8-bit w*h image, which it returns.
+ ignores input value 'pic8'
+ returns NULL on error
+
+ note: the rdisp,gdisp,bdisp arrays should be the 'displayed' colors,
+ not the 'desired' colors
+
+ if pic24 is NULL, uses the passed-in pic8 (an 8-bit image) as
+ the source, and the rmap,gmap,bmap arrays as the desired colors */
+
+ byte *np, *ep, *newpic;
+ short *cache;
+ int r2, g2, b2;
+ int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
+ int i, j, rerr, gerr, berr, pwide3;
+ int imax, jmax;
+ int key;
+ long cnt1, cnt2;
+ int error[512]; /* -255 .. 0 .. +255 */
+
+ /* compute somewhat non-linear floyd-steinberg error mapping table */
+ for (i = j = 0; i <= 0x40; i++, j++) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+ for ( /*empty*/ ; i < 0x80; i++, j += !(i & 1) ? 1 : 0) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+ for ( /*empty*/ ; i <= 0xff; i++) {
+ error[256 + i] = j;
+ error[256 - i] = -j;
+ }
+
+ cnt1 = cnt2 = 0;
+ pwide3 = w * 3;
+ imax = h - 1;
+ jmax = w - 1;
+ ep = (pic24) ? pic24 : pic8;
+
+ /* attempt to malloc things */
+ newpic = Blt_Malloc((size_t) (w * h));
+ cache = Blt_Calloc((size_t) (2 << 14), sizeof(short));
+ thisline = Blt_Malloc(pwide3 * sizeof(int));
+ nextline = Blt_Malloc(pwide3 * sizeof(int));
+ if (!cache || !newpic || !thisline || !nextline) {
+ if (newpic)
+ Blt_Free(newpic);
+ if (cache)
+ Blt_Free(cache);
+ if (thisline)
+ Blt_Free(thisline);
+ if (nextline)
+ Blt_Free(nextline);
+ return (byte *) NULL;
+ }
+ np = newpic;
+
+ /* Get first line of picture in reverse order. */
+
+ srcPtr = Blt_ColorImageBits(image), tempPtr = tempArr;
+ for (x = 0; x < width; x++, tempPtr++, srcPtr--) {
+ *tempPtr = *srcPtr;
+ }
+
+ for (y = 0; y < height; y++) {
+ tempPtr = curRowPtr, curRowPtr = nextRowPtr, nextRowPtr = tempPtr;
+
+ if (y != (height - 1)) {/* get next line */
+ for (x = 0; x < width; x++, tempPtr++, srcPtr--)
+ *tempPtr = *srcPtr;
+ }
+ }
+
+
+ Blt_Free(thisline);
+ Blt_Free(nextline);
+ Blt_Free(cache);
+
+ return newpic;
+}
+
+
+static void
+DitherImage(image)
+ Blt_ColorImage image;
+{
+ int width, height;
+
+
+
+}
+
+#endif
+
+#endif /* WIN32 */