Coverage Report

Created: 2025-09-04 06:28

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