gifquantize.c

Go to the documentation of this file.
00001 /* @(#)root/win32gdk:$Id: gifquantize.c 25744 2008-10-08 19:44:14Z bellenot $ */
00002 /* Author: Fons Rademakers   04/11/98*/
00003 /*****************************************************************************
00004 * Module to quantize high resolution image into lower one. You may want to   *
00005 * peek into the following article this code is based on:                     *
00006 * "Color Image Quantization for frame buffer Display", by Paul Heckbert      *
00007 * SIGGRAPH 1982 page 297-307.                                                *
00008 *****************************************************************************/
00009 
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 
00013 typedef unsigned char byte;
00014 typedef struct GifColorType {
00015     byte Red, Green, Blue;
00016 } GifColorType;
00017 
00018 #define ABS(x)  ((x) > 0 ? (x) : (-(x)))
00019 
00020 #define GIF_ERROR     0
00021 #define GIF_OK        1
00022 
00023 /* The colors are stripped to 5 bits per primary color */
00024 #define COLOR_ARRAY_SIZE 32768
00025 #define BITS_PER_PRIM_COLOR 5
00026 #define MAX_PRIM_COLOR      0x1f
00027 
00028 
00029 static int SortRGBAxis;
00030 
00031 typedef struct QuantizedColorType {
00032    byte RGB[3];
00033    byte NewColorIndex;
00034    long Count;
00035    struct QuantizedColorType *Pnext;
00036 } QuantizedColorType;
00037 
00038 typedef struct NewColorMapType {
00039    byte RGBMin[3], RGBWidth[3];
00040    unsigned int NumEntries;    /* # of QuantizedColorType in linked list below. */
00041    long Count;                 /* Total number of pixels in all the entries. */
00042    QuantizedColorType *QuantizedColors;
00043 } NewColorMapType;
00044 
00045 static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
00046                           unsigned int ColorMapSize,
00047                           unsigned int *NewColorMapSize);
00048 static int SortCmpRtn(const void *Entry1, const void *Entry2);
00049 
00050 
00051 /******************************************************************************
00052 * Quantize high resolution image into lower one. Input image consists of a    *
00053 * 2D array for each of the RGB colors with size Width by Height. There is no  *
00054 * Color map for the input. Output is a quantized image with 2D array of       *
00055 * indexes into the output color map.                                          *
00056 *   Note input image can be 24 bits at the most (8 for red/green/blue) and    *
00057 * the output has 256 colors at the most (256 entries in the color map.).      *
00058 * ColorMapSize specifies size of color map up to 256 and will be updated to   *
00059 * real size before returning.                                                 *
00060 *   Also non of the parameter are allocated by this routine.                  *
00061 *   This function returns GIF_OK if succesfull, GIF_ERROR otherwise.          *
00062 ******************************************************************************/
00063 int GIFquantize(unsigned int Width, unsigned int Height, int *ColorMapSize,
00064         byte *RedInput, byte *GreenInput, byte *BlueInput,
00065         byte *OutputBuffer, GifColorType *OutputColorMap)
00066 {
00067     unsigned int Index, NumOfEntries, newsize;
00068     int i, j, MaxRGBError[3];
00069     int NewColorMapSize;
00070     long Red, Green, Blue;
00071     NewColorMapType NewColorSubdiv[256];
00072     QuantizedColorType *ColorArrayEntries, *QuantizedColor;
00073 
00074     if ((ColorArrayEntries = (QuantizedColorType *)
00075             malloc(sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE)) == NULL) {
00076         fprintf(stderr, "QuantizeBuffer: not enough memory\n");
00077         return GIF_ERROR;
00078     }
00079 
00080     for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
00081         ColorArrayEntries[i].RGB[0]= i >> (2 * BITS_PER_PRIM_COLOR);
00082         ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR;
00083         ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
00084         ColorArrayEntries[i].Count = 0;
00085     }
00086 
00087     /* Sample the colors and their distribution: */
00088     for (i = 0; i < (int)(Width * Height); i++) {
00089         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
00090                     << (2 * BITS_PER_PRIM_COLOR)) +
00091                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
00092                     << BITS_PER_PRIM_COLOR) +
00093                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
00094         ColorArrayEntries[Index].Count++;
00095     }
00096 
00097     /* Put all the colors in the first entry of the color map, and call the  */
00098     /* recursive subdivision process.                                        */
00099     for (i = 0; i < 256; i++) {
00100         NewColorSubdiv[i].QuantizedColors = NULL;
00101         NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
00102         for (j = 0; j < 3; j++) {
00103             NewColorSubdiv[i].RGBMin[j] = 0;
00104             NewColorSubdiv[i].RGBWidth[j] = 255;
00105         }
00106     }
00107 
00108     /* Find the non empty entries in the color table and chain them: */
00109     for (i = 0; i < COLOR_ARRAY_SIZE; i++)
00110         if (ColorArrayEntries[i].Count > 0) break;
00111     QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
00112     NumOfEntries = 1;
00113     while (++i < COLOR_ARRAY_SIZE)
00114         if (ColorArrayEntries[i].Count > 0) {
00115             QuantizedColor -> Pnext = &ColorArrayEntries[i];
00116             QuantizedColor = &ColorArrayEntries[i];
00117             NumOfEntries++;
00118         }
00119     QuantizedColor -> Pnext = NULL;
00120 
00121     NewColorSubdiv[0].NumEntries = NumOfEntries;/* Different sampled colors. */
00122     NewColorSubdiv[0].Count = ((long) Width) * Height;            /* Pixels. */
00123     newsize = 1;
00124     if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &newsize) != GIF_OK) {
00125         free((char *) ColorArrayEntries);
00126         return GIF_ERROR;
00127     }
00128     NewColorMapSize = (int)newsize;
00129     if (NewColorMapSize < *ColorMapSize) {
00130         /* And clear rest of color map: */
00131         for (i = NewColorMapSize; i < *ColorMapSize; i++)
00132             OutputColorMap[i].Red =
00133             OutputColorMap[i].Green =
00134             OutputColorMap[i].Blue = 0;
00135     }
00136 
00137     /* Average the colors in each entry to be the color to be used in the    */
00138     /* output color map, and plug it into the output color map itself.       */
00139     for (i = 0; i < NewColorMapSize; i++) {
00140         if ((j = NewColorSubdiv[i].NumEntries) > 0) {
00141             QuantizedColor = NewColorSubdiv[i].QuantizedColors;
00142             Red = Green = Blue = 0;
00143             while (QuantizedColor) {
00144                 QuantizedColor -> NewColorIndex = i;
00145                 Red += QuantizedColor -> RGB[0];
00146                 Green += QuantizedColor -> RGB[1];
00147                 Blue += QuantizedColor -> RGB[2];
00148                 QuantizedColor = QuantizedColor -> Pnext;
00149             }
00150             OutputColorMap[i].Red = (byte)((Red << (8 - BITS_PER_PRIM_COLOR)) / j);
00151             OutputColorMap[i].Green = (byte)((Green << (8 - BITS_PER_PRIM_COLOR)) / j);
00152             OutputColorMap[i].Blue= (byte)((Blue << (8 - BITS_PER_PRIM_COLOR)) / j);
00153         }
00154         else
00155             fprintf(stderr, "Null entry in quantized color map - thats weird.");
00156     }
00157 
00158     /* Finally scan the input buffer again and put the mapped index in the   */
00159     /* output buffer.                                                        */
00160     MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
00161     for (i = 0; i < (int)(Width * Height); i++) {
00162         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
00163                     << (2 * BITS_PER_PRIM_COLOR)) +
00164                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
00165                     << BITS_PER_PRIM_COLOR) +
00166                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
00167         Index = ColorArrayEntries[Index].NewColorIndex;
00168         OutputBuffer[i] = Index;
00169         if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
00170             MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
00171         if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
00172             MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
00173         if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
00174             MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
00175     }
00176 
00177 #ifdef DEBUG
00178     fprintf(stderr,
00179             "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
00180                             MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
00181 #endif /* DEBUG */
00182 
00183     free((char *) ColorArrayEntries);
00184 
00185     *ColorMapSize = NewColorMapSize;
00186 
00187     return GIF_OK;
00188 }
00189 
00190 /******************************************************************************
00191 * Routine to subdivide the RGB space recursively using median cut in each     *
00192 * axes alternatingly until ColorMapSize different cubes exists.               *
00193 * The biggest cube in one dimension is subdivide unless it has only one entry.*
00194 * Returns GIF_ERROR if failed, otherwise GIF_OK.                              *
00195 ******************************************************************************/
00196 static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
00197                           unsigned int ColorMapSize,
00198                           unsigned int *NewColorMapSize)
00199 {
00200     int MaxSize;
00201     unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor;
00202     long Sum, Count;
00203     QuantizedColorType *QuantizedColor, **SortArray;
00204 
00205     while (ColorMapSize > *NewColorMapSize) {
00206         /* Find candidate for subdivision: */
00207         MaxSize = -1;
00208         for (i = 0; i < *NewColorMapSize; i++) {
00209             for (j = 0; j < 3; j++) {
00210                 if (((int) NewColorSubdiv[i].RGBWidth[j]) > MaxSize &&
00211                     NewColorSubdiv[i].NumEntries > 1) {
00212                     MaxSize = NewColorSubdiv[i].RGBWidth[j];
00213                     Index = i;
00214                     SortRGBAxis = j;
00215                 }
00216             }
00217         }
00218 
00219         if (MaxSize == -1)
00220             return GIF_OK;
00221 
00222         /* Split the entry Index into two along the axis SortRGBAxis: */
00223 
00224         /* Sort all elements in that entry along the given axis and split at */
00225         /* the median.                                                       */
00226         if ((SortArray = (QuantizedColorType **)
00227             malloc(sizeof(QuantizedColorType *) *
00228                    NewColorSubdiv[Index].NumEntries)) == NULL)
00229                 return GIF_ERROR;
00230         for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
00231              j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
00232              j++, QuantizedColor = QuantizedColor -> Pnext)
00233             SortArray[j] = QuantizedColor;
00234         qsort(SortArray, NewColorSubdiv[Index].NumEntries,
00235               sizeof(QuantizedColorType *), SortCmpRtn);
00236 
00237         /* Relink the sorted list into one: */
00238         for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
00239             SortArray[j] -> Pnext = SortArray[j + 1];
00240         SortArray[NewColorSubdiv[Index].NumEntries - 1] -> Pnext = NULL;
00241         NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
00242         free((char *) SortArray);
00243 
00244         /* Now simply add the Counts until we have half of the Count: */
00245         Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor -> Count;
00246         NumEntries = 1;
00247         Count = QuantizedColor -> Count;
00248         while ((Sum -= QuantizedColor -> Pnext -> Count) >= 0 &&
00249                QuantizedColor -> Pnext != NULL &&
00250                QuantizedColor -> Pnext -> Pnext != NULL) {
00251             QuantizedColor = QuantizedColor -> Pnext;
00252             NumEntries++;
00253             Count += QuantizedColor -> Count;
00254         }
00255         /* Save the values of the last color of the first half, and first    */
00256         /* of the second half so we can update the Bounding Boxes later.     */
00257         /* Also as the colors are quantized and the BBoxes are full 0..255,  */
00258         /* they need to be rescaled.                                         */
00259         MaxColor = QuantizedColor -> RGB[SortRGBAxis];/* Max. of first half. */
00260         MinColor = QuantizedColor -> Pnext -> RGB[SortRGBAxis];/* of second. */
00261         MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
00262         MinColor <<= (8 - BITS_PER_PRIM_COLOR);
00263 
00264         /* Partition right here: */
00265         NewColorSubdiv[*NewColorMapSize].QuantizedColors =
00266             QuantizedColor -> Pnext;
00267         QuantizedColor -> Pnext = NULL;
00268         NewColorSubdiv[*NewColorMapSize].Count = Count;
00269         NewColorSubdiv[Index].Count -= Count;
00270         NewColorSubdiv[*NewColorMapSize].NumEntries =
00271             NewColorSubdiv[Index].NumEntries - NumEntries;
00272         NewColorSubdiv[Index].NumEntries = NumEntries;
00273         for (j = 0; j < 3; j++) {
00274             NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
00275                 NewColorSubdiv[Index].RGBMin[j];
00276             NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
00277                 NewColorSubdiv[Index].RGBWidth[j];
00278         }
00279         NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
00280             NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
00281             NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] -
00282             MinColor;
00283         NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
00284 
00285         NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
00286             MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
00287 
00288         (*NewColorMapSize)++;
00289     }
00290 
00291     return GIF_OK;
00292 }
00293 
00294 /******************************************************************************
00295 * Routine called by qsort to compare to entries.                              *
00296 ******************************************************************************/
00297 static int SortCmpRtn(const void *Entry1, const void *Entry2)
00298 {
00299     return (* ((QuantizedColorType **) Entry1)) -> RGB[SortRGBAxis] -
00300            (* ((QuantizedColorType **) Entry2)) -> RGB[SortRGBAxis];
00301 }

Generated on Tue Jul 5 14:14:44 2011 for ROOT_528-00b_version by  doxygen 1.5.1