1*324bb76bSAndroid Build Coastguard Worker /*****************************************************************************
2*324bb76bSAndroid Build Coastguard Worker
3*324bb76bSAndroid Build Coastguard Worker quantize.c - quantize a high resolution image into lower one
4*324bb76bSAndroid Build Coastguard Worker
5*324bb76bSAndroid Build Coastguard Worker Based on: "Color Image Quantization for frame buffer Display", by
6*324bb76bSAndroid Build Coastguard Worker Paul Heckbert SIGGRAPH 1982 page 297-307.
7*324bb76bSAndroid Build Coastguard Worker
8*324bb76bSAndroid Build Coastguard Worker This doesn't really belong in the core library, was undocumented,
9*324bb76bSAndroid Build Coastguard Worker and was removed in 4.2. Then it turned out some client apps were
10*324bb76bSAndroid Build Coastguard Worker actually using it, so it was restored in 5.0.
11*324bb76bSAndroid Build Coastguard Worker
12*324bb76bSAndroid Build Coastguard Worker SPDX-License-Identifier: MIT
13*324bb76bSAndroid Build Coastguard Worker
14*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
15*324bb76bSAndroid Build Coastguard Worker
16*324bb76bSAndroid Build Coastguard Worker #include <stdio.h>
17*324bb76bSAndroid Build Coastguard Worker #include <stdlib.h>
18*324bb76bSAndroid Build Coastguard Worker
19*324bb76bSAndroid Build Coastguard Worker #include "gif_lib.h"
20*324bb76bSAndroid Build Coastguard Worker #include "gif_lib_private.h"
21*324bb76bSAndroid Build Coastguard Worker
22*324bb76bSAndroid Build Coastguard Worker #define ABS(x) ((x) > 0 ? (x) : (-(x)))
23*324bb76bSAndroid Build Coastguard Worker
24*324bb76bSAndroid Build Coastguard Worker #define COLOR_ARRAY_SIZE 32768
25*324bb76bSAndroid Build Coastguard Worker #define BITS_PER_PRIM_COLOR 5
26*324bb76bSAndroid Build Coastguard Worker #define MAX_PRIM_COLOR 0x1f
27*324bb76bSAndroid Build Coastguard Worker
28*324bb76bSAndroid Build Coastguard Worker static int SortRGBAxis;
29*324bb76bSAndroid Build Coastguard Worker
30*324bb76bSAndroid Build Coastguard Worker typedef struct QuantizedColorType {
31*324bb76bSAndroid Build Coastguard Worker GifByteType RGB[3];
32*324bb76bSAndroid Build Coastguard Worker GifByteType NewColorIndex;
33*324bb76bSAndroid Build Coastguard Worker long Count;
34*324bb76bSAndroid Build Coastguard Worker struct QuantizedColorType *Pnext;
35*324bb76bSAndroid Build Coastguard Worker } QuantizedColorType;
36*324bb76bSAndroid Build Coastguard Worker
37*324bb76bSAndroid Build Coastguard Worker typedef struct NewColorMapType {
38*324bb76bSAndroid Build Coastguard Worker GifByteType RGBMin[3], RGBWidth[3];
39*324bb76bSAndroid Build Coastguard Worker unsigned int
40*324bb76bSAndroid Build Coastguard Worker NumEntries; /* # of QuantizedColorType in linked list below */
41*324bb76bSAndroid Build Coastguard Worker unsigned long Count; /* Total number of pixels in all the entries */
42*324bb76bSAndroid Build Coastguard Worker QuantizedColorType *QuantizedColors;
43*324bb76bSAndroid Build Coastguard Worker } NewColorMapType;
44*324bb76bSAndroid Build Coastguard Worker
45*324bb76bSAndroid Build Coastguard Worker static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
46*324bb76bSAndroid Build Coastguard Worker unsigned int ColorMapSize,
47*324bb76bSAndroid Build Coastguard Worker unsigned int *NewColorMapSize);
48*324bb76bSAndroid Build Coastguard Worker static int SortCmpRtn(const void *Entry1, const void *Entry2);
49*324bb76bSAndroid Build Coastguard Worker
50*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
51*324bb76bSAndroid Build Coastguard Worker Quantize high resolution image into lower one. Input image consists of a
52*324bb76bSAndroid Build Coastguard Worker 2D array for each of the RGB colors with size Width by Height. There is no
53*324bb76bSAndroid Build Coastguard Worker Color map for the input. Output is a quantized image with 2D array of
54*324bb76bSAndroid Build Coastguard Worker indexes into the output color map.
55*324bb76bSAndroid Build Coastguard Worker Note input image can be 24 bits at the most (8 for red/green/blue) and
56*324bb76bSAndroid Build Coastguard Worker the output has 256 colors at the most (256 entries in the color map.).
57*324bb76bSAndroid Build Coastguard Worker ColorMapSize specifies size of color map up to 256 and will be updated to
58*324bb76bSAndroid Build Coastguard Worker real size before returning.
59*324bb76bSAndroid Build Coastguard Worker Also non of the parameter are allocated by this routine.
60*324bb76bSAndroid Build Coastguard Worker This function returns GIF_OK if successful, GIF_ERROR otherwise.
61*324bb76bSAndroid Build Coastguard Worker ******************************************************************************/
GifQuantizeBuffer(unsigned int Width,unsigned int Height,int * ColorMapSize,const GifByteType * RedInput,const GifByteType * GreenInput,const GifByteType * BlueInput,GifByteType * OutputBuffer,GifColorType * OutputColorMap)62*324bb76bSAndroid Build Coastguard Worker int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
63*324bb76bSAndroid Build Coastguard Worker int *ColorMapSize, const GifByteType *RedInput,
64*324bb76bSAndroid Build Coastguard Worker const GifByteType *GreenInput,
65*324bb76bSAndroid Build Coastguard Worker const GifByteType *BlueInput, GifByteType *OutputBuffer,
66*324bb76bSAndroid Build Coastguard Worker GifColorType *OutputColorMap) {
67*324bb76bSAndroid Build Coastguard Worker
68*324bb76bSAndroid Build Coastguard Worker unsigned int Index, NumOfEntries;
69*324bb76bSAndroid Build Coastguard Worker int i, j, MaxRGBError[3];
70*324bb76bSAndroid Build Coastguard Worker unsigned int NewColorMapSize;
71*324bb76bSAndroid Build Coastguard Worker long Red, Green, Blue;
72*324bb76bSAndroid Build Coastguard Worker NewColorMapType NewColorSubdiv[256];
73*324bb76bSAndroid Build Coastguard Worker QuantizedColorType *ColorArrayEntries, *QuantizedColor;
74*324bb76bSAndroid Build Coastguard Worker
75*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries = (QuantizedColorType *)malloc(
76*324bb76bSAndroid Build Coastguard Worker sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
77*324bb76bSAndroid Build Coastguard Worker if (ColorArrayEntries == NULL) {
78*324bb76bSAndroid Build Coastguard Worker return GIF_ERROR;
79*324bb76bSAndroid Build Coastguard Worker }
80*324bb76bSAndroid Build Coastguard Worker
81*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
82*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
83*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries[i].RGB[1] =
84*324bb76bSAndroid Build Coastguard Worker (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR;
85*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
86*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries[i].Count = 0;
87*324bb76bSAndroid Build Coastguard Worker }
88*324bb76bSAndroid Build Coastguard Worker
89*324bb76bSAndroid Build Coastguard Worker /* Sample the colors and their distribution: */
90*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < (int)(Width * Height); i++) {
91*324bb76bSAndroid Build Coastguard Worker Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
92*324bb76bSAndroid Build Coastguard Worker << (2 * BITS_PER_PRIM_COLOR)) +
93*324bb76bSAndroid Build Coastguard Worker ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
94*324bb76bSAndroid Build Coastguard Worker << BITS_PER_PRIM_COLOR) +
95*324bb76bSAndroid Build Coastguard Worker (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
96*324bb76bSAndroid Build Coastguard Worker ColorArrayEntries[Index].Count++;
97*324bb76bSAndroid Build Coastguard Worker }
98*324bb76bSAndroid Build Coastguard Worker
99*324bb76bSAndroid Build Coastguard Worker /* Put all the colors in the first entry of the color map, and call the
100*324bb76bSAndroid Build Coastguard Worker * recursive subdivision process. */
101*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < 256; i++) {
102*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[i].QuantizedColors = NULL;
103*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
104*324bb76bSAndroid Build Coastguard Worker for (j = 0; j < 3; j++) {
105*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[i].RGBMin[j] = 0;
106*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[i].RGBWidth[j] = 255;
107*324bb76bSAndroid Build Coastguard Worker }
108*324bb76bSAndroid Build Coastguard Worker }
109*324bb76bSAndroid Build Coastguard Worker
110*324bb76bSAndroid Build Coastguard Worker /* Find the non empty entries in the color table and chain them: */
111*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
112*324bb76bSAndroid Build Coastguard Worker if (ColorArrayEntries[i].Count > 0) {
113*324bb76bSAndroid Build Coastguard Worker break;
114*324bb76bSAndroid Build Coastguard Worker }
115*324bb76bSAndroid Build Coastguard Worker }
116*324bb76bSAndroid Build Coastguard Worker QuantizedColor = NewColorSubdiv[0].QuantizedColors =
117*324bb76bSAndroid Build Coastguard Worker &ColorArrayEntries[i];
118*324bb76bSAndroid Build Coastguard Worker NumOfEntries = 1;
119*324bb76bSAndroid Build Coastguard Worker while (++i < COLOR_ARRAY_SIZE) {
120*324bb76bSAndroid Build Coastguard Worker if (ColorArrayEntries[i].Count > 0) {
121*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext = &ColorArrayEntries[i];
122*324bb76bSAndroid Build Coastguard Worker QuantizedColor = &ColorArrayEntries[i];
123*324bb76bSAndroid Build Coastguard Worker NumOfEntries++;
124*324bb76bSAndroid Build Coastguard Worker }
125*324bb76bSAndroid Build Coastguard Worker }
126*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext = NULL;
127*324bb76bSAndroid Build Coastguard Worker
128*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[0].NumEntries =
129*324bb76bSAndroid Build Coastguard Worker NumOfEntries; /* Different sampled colors */
130*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
131*324bb76bSAndroid Build Coastguard Worker NewColorMapSize = 1;
132*324bb76bSAndroid Build Coastguard Worker if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
133*324bb76bSAndroid Build Coastguard Worker GIF_OK) {
134*324bb76bSAndroid Build Coastguard Worker free((char *)ColorArrayEntries);
135*324bb76bSAndroid Build Coastguard Worker return GIF_ERROR;
136*324bb76bSAndroid Build Coastguard Worker }
137*324bb76bSAndroid Build Coastguard Worker if (NewColorMapSize < *ColorMapSize) {
138*324bb76bSAndroid Build Coastguard Worker /* And clear rest of color map: */
139*324bb76bSAndroid Build Coastguard Worker for (i = NewColorMapSize; i < *ColorMapSize; i++) {
140*324bb76bSAndroid Build Coastguard Worker OutputColorMap[i].Red = OutputColorMap[i].Green =
141*324bb76bSAndroid Build Coastguard Worker OutputColorMap[i].Blue = 0;
142*324bb76bSAndroid Build Coastguard Worker }
143*324bb76bSAndroid Build Coastguard Worker }
144*324bb76bSAndroid Build Coastguard Worker
145*324bb76bSAndroid Build Coastguard Worker /* Average the colors in each entry to be the color to be used in the
146*324bb76bSAndroid Build Coastguard Worker * output color map, and plug it into the output color map itself. */
147*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < NewColorMapSize; i++) {
148*324bb76bSAndroid Build Coastguard Worker if ((j = NewColorSubdiv[i].NumEntries) > 0) {
149*324bb76bSAndroid Build Coastguard Worker QuantizedColor = NewColorSubdiv[i].QuantizedColors;
150*324bb76bSAndroid Build Coastguard Worker Red = Green = Blue = 0;
151*324bb76bSAndroid Build Coastguard Worker while (QuantizedColor) {
152*324bb76bSAndroid Build Coastguard Worker QuantizedColor->NewColorIndex = i;
153*324bb76bSAndroid Build Coastguard Worker Red += QuantizedColor->RGB[0];
154*324bb76bSAndroid Build Coastguard Worker Green += QuantizedColor->RGB[1];
155*324bb76bSAndroid Build Coastguard Worker Blue += QuantizedColor->RGB[2];
156*324bb76bSAndroid Build Coastguard Worker QuantizedColor = QuantizedColor->Pnext;
157*324bb76bSAndroid Build Coastguard Worker }
158*324bb76bSAndroid Build Coastguard Worker OutputColorMap[i].Red =
159*324bb76bSAndroid Build Coastguard Worker (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
160*324bb76bSAndroid Build Coastguard Worker OutputColorMap[i].Green =
161*324bb76bSAndroid Build Coastguard Worker (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
162*324bb76bSAndroid Build Coastguard Worker OutputColorMap[i].Blue =
163*324bb76bSAndroid Build Coastguard Worker (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
164*324bb76bSAndroid Build Coastguard Worker }
165*324bb76bSAndroid Build Coastguard Worker }
166*324bb76bSAndroid Build Coastguard Worker
167*324bb76bSAndroid Build Coastguard Worker /* Finally scan the input buffer again and put the mapped index in the
168*324bb76bSAndroid Build Coastguard Worker * output buffer. */
169*324bb76bSAndroid Build Coastguard Worker MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
170*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < (int)(Width * Height); i++) {
171*324bb76bSAndroid Build Coastguard Worker Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
172*324bb76bSAndroid Build Coastguard Worker << (2 * BITS_PER_PRIM_COLOR)) +
173*324bb76bSAndroid Build Coastguard Worker ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
174*324bb76bSAndroid Build Coastguard Worker << BITS_PER_PRIM_COLOR) +
175*324bb76bSAndroid Build Coastguard Worker (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
176*324bb76bSAndroid Build Coastguard Worker Index = ColorArrayEntries[Index].NewColorIndex;
177*324bb76bSAndroid Build Coastguard Worker OutputBuffer[i] = Index;
178*324bb76bSAndroid Build Coastguard Worker if (MaxRGBError[0] <
179*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Red - RedInput[i])) {
180*324bb76bSAndroid Build Coastguard Worker MaxRGBError[0] =
181*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Red - RedInput[i]);
182*324bb76bSAndroid Build Coastguard Worker }
183*324bb76bSAndroid Build Coastguard Worker if (MaxRGBError[1] <
184*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Green - GreenInput[i])) {
185*324bb76bSAndroid Build Coastguard Worker MaxRGBError[1] =
186*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Green - GreenInput[i]);
187*324bb76bSAndroid Build Coastguard Worker }
188*324bb76bSAndroid Build Coastguard Worker if (MaxRGBError[2] <
189*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Blue - BlueInput[i])) {
190*324bb76bSAndroid Build Coastguard Worker MaxRGBError[2] =
191*324bb76bSAndroid Build Coastguard Worker ABS(OutputColorMap[Index].Blue - BlueInput[i]);
192*324bb76bSAndroid Build Coastguard Worker }
193*324bb76bSAndroid Build Coastguard Worker }
194*324bb76bSAndroid Build Coastguard Worker
195*324bb76bSAndroid Build Coastguard Worker #ifdef DEBUG
196*324bb76bSAndroid Build Coastguard Worker fprintf(stderr,
197*324bb76bSAndroid Build Coastguard Worker "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
198*324bb76bSAndroid Build Coastguard Worker MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
199*324bb76bSAndroid Build Coastguard Worker #endif /* DEBUG */
200*324bb76bSAndroid Build Coastguard Worker
201*324bb76bSAndroid Build Coastguard Worker free((char *)ColorArrayEntries);
202*324bb76bSAndroid Build Coastguard Worker
203*324bb76bSAndroid Build Coastguard Worker *ColorMapSize = NewColorMapSize;
204*324bb76bSAndroid Build Coastguard Worker
205*324bb76bSAndroid Build Coastguard Worker return GIF_OK;
206*324bb76bSAndroid Build Coastguard Worker }
207*324bb76bSAndroid Build Coastguard Worker
208*324bb76bSAndroid Build Coastguard Worker /******************************************************************************
209*324bb76bSAndroid Build Coastguard Worker Routine to subdivide the RGB space recursively using median cut in each
210*324bb76bSAndroid Build Coastguard Worker axes alternatingly until ColorMapSize different cubes exists.
211*324bb76bSAndroid Build Coastguard Worker The biggest cube in one dimension is subdivide unless it has only one entry.
212*324bb76bSAndroid Build Coastguard Worker Returns GIF_ERROR if failed, otherwise GIF_OK.
213*324bb76bSAndroid Build Coastguard Worker *******************************************************************************/
SubdivColorMap(NewColorMapType * NewColorSubdiv,unsigned int ColorMapSize,unsigned int * NewColorMapSize)214*324bb76bSAndroid Build Coastguard Worker static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
215*324bb76bSAndroid Build Coastguard Worker unsigned int ColorMapSize,
216*324bb76bSAndroid Build Coastguard Worker unsigned int *NewColorMapSize) {
217*324bb76bSAndroid Build Coastguard Worker
218*324bb76bSAndroid Build Coastguard Worker unsigned int i, j, Index = 0;
219*324bb76bSAndroid Build Coastguard Worker QuantizedColorType *QuantizedColor, **SortArray;
220*324bb76bSAndroid Build Coastguard Worker
221*324bb76bSAndroid Build Coastguard Worker while (ColorMapSize > *NewColorMapSize) {
222*324bb76bSAndroid Build Coastguard Worker /* Find candidate for subdivision: */
223*324bb76bSAndroid Build Coastguard Worker long Sum, Count;
224*324bb76bSAndroid Build Coastguard Worker int MaxSize = -1;
225*324bb76bSAndroid Build Coastguard Worker unsigned int NumEntries, MinColor, MaxColor;
226*324bb76bSAndroid Build Coastguard Worker for (i = 0; i < *NewColorMapSize; i++) {
227*324bb76bSAndroid Build Coastguard Worker for (j = 0; j < 3; j++) {
228*324bb76bSAndroid Build Coastguard Worker if ((((int)NewColorSubdiv[i].RGBWidth[j]) >
229*324bb76bSAndroid Build Coastguard Worker MaxSize) &&
230*324bb76bSAndroid Build Coastguard Worker (NewColorSubdiv[i].NumEntries > 1)) {
231*324bb76bSAndroid Build Coastguard Worker MaxSize = NewColorSubdiv[i].RGBWidth[j];
232*324bb76bSAndroid Build Coastguard Worker Index = i;
233*324bb76bSAndroid Build Coastguard Worker SortRGBAxis = j;
234*324bb76bSAndroid Build Coastguard Worker }
235*324bb76bSAndroid Build Coastguard Worker }
236*324bb76bSAndroid Build Coastguard Worker }
237*324bb76bSAndroid Build Coastguard Worker
238*324bb76bSAndroid Build Coastguard Worker if (MaxSize == -1) {
239*324bb76bSAndroid Build Coastguard Worker return GIF_OK;
240*324bb76bSAndroid Build Coastguard Worker }
241*324bb76bSAndroid Build Coastguard Worker
242*324bb76bSAndroid Build Coastguard Worker /* Split the entry Index into two along the axis SortRGBAxis: */
243*324bb76bSAndroid Build Coastguard Worker
244*324bb76bSAndroid Build Coastguard Worker /* Sort all elements in that entry along the given axis and
245*324bb76bSAndroid Build Coastguard Worker * split at the median. */
246*324bb76bSAndroid Build Coastguard Worker SortArray = (QuantizedColorType **)malloc(
247*324bb76bSAndroid Build Coastguard Worker sizeof(QuantizedColorType *) *
248*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].NumEntries);
249*324bb76bSAndroid Build Coastguard Worker if (SortArray == NULL) {
250*324bb76bSAndroid Build Coastguard Worker return GIF_ERROR;
251*324bb76bSAndroid Build Coastguard Worker }
252*324bb76bSAndroid Build Coastguard Worker for (j = 0,
253*324bb76bSAndroid Build Coastguard Worker QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
254*324bb76bSAndroid Build Coastguard Worker j < NewColorSubdiv[Index].NumEntries &&
255*324bb76bSAndroid Build Coastguard Worker QuantizedColor != NULL;
256*324bb76bSAndroid Build Coastguard Worker j++, QuantizedColor = QuantizedColor->Pnext) {
257*324bb76bSAndroid Build Coastguard Worker SortArray[j] = QuantizedColor;
258*324bb76bSAndroid Build Coastguard Worker }
259*324bb76bSAndroid Build Coastguard Worker
260*324bb76bSAndroid Build Coastguard Worker /*
261*324bb76bSAndroid Build Coastguard Worker * Because qsort isn't stable, this can produce differing
262*324bb76bSAndroid Build Coastguard Worker * results for the order of tuples depending on platform
263*324bb76bSAndroid Build Coastguard Worker * details of how qsort() is implemented.
264*324bb76bSAndroid Build Coastguard Worker *
265*324bb76bSAndroid Build Coastguard Worker * We mitigate this problem by sorting on all three axes rather
266*324bb76bSAndroid Build Coastguard Worker * than only the one specied by SortRGBAxis; that way the
267*324bb76bSAndroid Build Coastguard Worker * instability can only become an issue if there are multiple
268*324bb76bSAndroid Build Coastguard Worker * color indices referring to identical RGB tuples. Older
269*324bb76bSAndroid Build Coastguard Worker * versions of this sorted on only the one axis.
270*324bb76bSAndroid Build Coastguard Worker */
271*324bb76bSAndroid Build Coastguard Worker qsort(SortArray, NewColorSubdiv[Index].NumEntries,
272*324bb76bSAndroid Build Coastguard Worker sizeof(QuantizedColorType *), SortCmpRtn);
273*324bb76bSAndroid Build Coastguard Worker
274*324bb76bSAndroid Build Coastguard Worker /* Relink the sorted list into one: */
275*324bb76bSAndroid Build Coastguard Worker for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) {
276*324bb76bSAndroid Build Coastguard Worker SortArray[j]->Pnext = SortArray[j + 1];
277*324bb76bSAndroid Build Coastguard Worker }
278*324bb76bSAndroid Build Coastguard Worker SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
279*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].QuantizedColors = QuantizedColor =
280*324bb76bSAndroid Build Coastguard Worker SortArray[0];
281*324bb76bSAndroid Build Coastguard Worker free((char *)SortArray);
282*324bb76bSAndroid Build Coastguard Worker
283*324bb76bSAndroid Build Coastguard Worker /* Now simply add the Counts until we have half of the Count: */
284*324bb76bSAndroid Build Coastguard Worker Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
285*324bb76bSAndroid Build Coastguard Worker NumEntries = 1;
286*324bb76bSAndroid Build Coastguard Worker Count = QuantizedColor->Count;
287*324bb76bSAndroid Build Coastguard Worker while (QuantizedColor->Pnext != NULL &&
288*324bb76bSAndroid Build Coastguard Worker (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
289*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext->Pnext != NULL) {
290*324bb76bSAndroid Build Coastguard Worker QuantizedColor = QuantizedColor->Pnext;
291*324bb76bSAndroid Build Coastguard Worker NumEntries++;
292*324bb76bSAndroid Build Coastguard Worker Count += QuantizedColor->Count;
293*324bb76bSAndroid Build Coastguard Worker }
294*324bb76bSAndroid Build Coastguard Worker /* Save the values of the last color of the first half, and
295*324bb76bSAndroid Build Coastguard Worker * first of the second half so we can update the Bounding Boxes
296*324bb76bSAndroid Build Coastguard Worker * later. Also as the colors are quantized and the BBoxes are
297*324bb76bSAndroid Build Coastguard Worker * full 0..255, they need to be rescaled.
298*324bb76bSAndroid Build Coastguard Worker */
299*324bb76bSAndroid Build Coastguard Worker MaxColor =
300*324bb76bSAndroid Build Coastguard Worker QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
301*324bb76bSAndroid Build Coastguard Worker /* coverity[var_deref_op] */
302*324bb76bSAndroid Build Coastguard Worker MinColor =
303*324bb76bSAndroid Build Coastguard Worker // cppcheck-suppress nullPointerRedundantCheck
304*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
305*324bb76bSAndroid Build Coastguard Worker MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
306*324bb76bSAndroid Build Coastguard Worker MinColor <<= (8 - BITS_PER_PRIM_COLOR);
307*324bb76bSAndroid Build Coastguard Worker
308*324bb76bSAndroid Build Coastguard Worker /* Partition right here: */
309*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].QuantizedColors =
310*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext;
311*324bb76bSAndroid Build Coastguard Worker QuantizedColor->Pnext = NULL;
312*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].Count = Count;
313*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].Count -= Count;
314*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].NumEntries =
315*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].NumEntries - NumEntries;
316*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].NumEntries = NumEntries;
317*324bb76bSAndroid Build Coastguard Worker for (j = 0; j < 3; j++) {
318*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
319*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].RGBMin[j];
320*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
321*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].RGBWidth[j];
322*324bb76bSAndroid Build Coastguard Worker }
323*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
324*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
325*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] -
326*324bb76bSAndroid Build Coastguard Worker MinColor;
327*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
328*324bb76bSAndroid Build Coastguard Worker
329*324bb76bSAndroid Build Coastguard Worker NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
330*324bb76bSAndroid Build Coastguard Worker MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
331*324bb76bSAndroid Build Coastguard Worker
332*324bb76bSAndroid Build Coastguard Worker (*NewColorMapSize)++;
333*324bb76bSAndroid Build Coastguard Worker }
334*324bb76bSAndroid Build Coastguard Worker
335*324bb76bSAndroid Build Coastguard Worker return GIF_OK;
336*324bb76bSAndroid Build Coastguard Worker }
337*324bb76bSAndroid Build Coastguard Worker
338*324bb76bSAndroid Build Coastguard Worker /****************************************************************************
339*324bb76bSAndroid Build Coastguard Worker Routine called by qsort to compare two entries.
340*324bb76bSAndroid Build Coastguard Worker *****************************************************************************/
341*324bb76bSAndroid Build Coastguard Worker
SortCmpRtn(const void * Entry1,const void * Entry2)342*324bb76bSAndroid Build Coastguard Worker static int SortCmpRtn(const void *Entry1, const void *Entry2) {
343*324bb76bSAndroid Build Coastguard Worker QuantizedColorType *entry1 = (*((QuantizedColorType **)Entry1));
344*324bb76bSAndroid Build Coastguard Worker QuantizedColorType *entry2 = (*((QuantizedColorType **)Entry2));
345*324bb76bSAndroid Build Coastguard Worker
346*324bb76bSAndroid Build Coastguard Worker /* sort on all axes of the color space! */
347*324bb76bSAndroid Build Coastguard Worker int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 +
348*324bb76bSAndroid Build Coastguard Worker entry1->RGB[(SortRGBAxis + 1) % 3] * 256 +
349*324bb76bSAndroid Build Coastguard Worker entry1->RGB[(SortRGBAxis + 2) % 3];
350*324bb76bSAndroid Build Coastguard Worker int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 +
351*324bb76bSAndroid Build Coastguard Worker entry2->RGB[(SortRGBAxis + 1) % 3] * 256 +
352*324bb76bSAndroid Build Coastguard Worker entry2->RGB[(SortRGBAxis + 2) % 3];
353*324bb76bSAndroid Build Coastguard Worker
354*324bb76bSAndroid Build Coastguard Worker return hash1 - hash2;
355*324bb76bSAndroid Build Coastguard Worker }
356*324bb76bSAndroid Build Coastguard Worker
357*324bb76bSAndroid Build Coastguard Worker /* end */
358