xref: /aosp_15_r20/external/giflib/quantize.c (revision 324bb76b8d05e2a05aa88511fff61cf3f9ca5892)
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